apk-reverse について
apk-reverseは、Android APKのリバースエンジニアリングとパッチ作成を手順化したスキルで、変更後の動作を検証したインストール可能な成果物を重視します。開発者向けに、対象の分類、ツール選定、統制された実験、対応範囲の明示を案内します。
主な機能
- 01パッチ前の対象分類と成果物要件の確認
- 02利用可能なツール機能の調査と選定
- 03対照ビルドと単一変数の実験
- 04インストールと機能単位の検証
- 05対応範囲と中止条件の明示
活用シーン
- 01アプリの動作を担うレイヤーの特定
- 02対照ビルドを伴うパッチ計画
- 03変更したAPKの端末上での検証
- 04対象が文書化された対応範囲内かの判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ newliver666/ apk-reverse --skill apk-reverseインストール後、Claude Code で/apk-reverseを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonevidence/capability-matrix.jsonknown-limitations.mdtested-tool-versions.jsonreferences/account-gates.mdad-removal.mdadvanced-unpacking.mdbyte-level-patching.mdcode-virtualization-and-custom-linkers.mdcoverage-and-limits.mddart-aot.mddesensitization-and-leak-scans.mddetection-and-anti-analysis.mddex-patching.mddexsql-sql-query-layer.mddynamic-frida.mdemulation-and-rpc.mdenvironment.mdete-diff-tracing.mdevidence-summary.mdframework-runtimes.mdhandoff-boundaries.mdhardware-sampling-and-crash.mdhardware-watchpoints.mdjava2c-and-jni-sinking.mdkernel-and-environment-hardening.mdkernel-event-injection.mdlong-task-discipline.mdlsposed-and-modules.mdreferences/coverage/route-inventory.md詳細
APK のリバースエンジニアリングとパッチ適用
目標:検証済みで、インストール可能かつ、引き続き正常に動作する成果物をすばやく作り、見た目にはまったく問題がないのに APK を壊してしまう一連のミスを避けます。
まずはこれを実行 — 何よりも先に、次の4つを行ってください
このファイルは手順とゲートを定めたものであり、背景説明ではありません。また、長いため途中が読み飛ばされがちです。そこで、まず4つの手順を示し、以下でその理由を説明します。
1. 手段を選ぶ前に分類してください。 §Start here の13個の質問に答えてください。R4によって、そもそも適切な層を編集しているかどうかが決まります。誤った分岐を選んでも、はっきりと失敗するとは限りません。ビルドできて、実行もできるのに、意図しない動作をする成果物ができあがります。
2. §Gates に記載された合格基準に従い、4つのゲートを順にクリアしてください。 G1 では作業を始める前に、成果物の形式を検証可能な一文で定めます。G2 では、このマシンで実際に何ができるかを確認します。G3 ではコードの場所を特定します。G4 ではベースラインと対照を記録します。「考え方は理解した」だけでは、ゲートをクリアしたことにはなりません。
3. 説明できない症状は、謎解きではなく停止の合図です。 次の試行をする前に、§Symptom index で症状の形を探し、その行で指定されたファイルを読み込んでください。各項目は、調査に何時間もかかった経験から記載されています。いくつかのケースでは、原因を再び突き止めようとする前に、すでに答えが書き残されていました。
4. 同じ形の試行で2回失敗したら、3つ目の方法を試すのではなく、分類に戻ってください(§Stop conditions)。また、§What "done" means にある6つの項目を満たさずに、完了したと報告することは絶対に避けてください。
このファイル自体に関する2つのルール。 失敗の原因が計画と合わない場合、次に行うべきことは症状インデックスを確認することであり、さらに考え続けることではありません。また、このファイルの記述と自分の推論が食い違う場合、覆す証拠が得られるまでは、このファイルに従ってください。ここにある各ルールは、何時間もの損失につながった失敗の積み重ねから得られたものです。いまの直感は、まだその失敗に遭遇していない人の直感です。
他のすべてに優先する4つのルール
R1 — 対象に手を加える前に、成果物を検証可能な一文で定義してください。
「動くこと」が目標ではありません。「指定された制約のもとで動くこと」が目標です。root 権限を使った方法、実行時インストルメンテーション、ホスト側プロキシ、パッチ適用済みデバイスでの結果は、通常のスマートフォンにインストールして動作する成果物という要件を満たさないことがよくあります。それでも、そのような結果を完成品として提示してしまいがちです。一文を書き、各チェックポイントで読み直してください。要件を満たせない場合は、率直に伝え、特権を必要とする回避策には、成果物ではなくフォールバックと明記してください。
→ references/long-task-discipline.md §the most expensive drift
R2 — 変数は一度に1つだけ変更し、対照ビルドを保持してください。
2つの要素を同時に切り替える実験は、失敗しても何も学べません。原因を特定できない失敗は、誤った原因に結び付けられてしまいます。「アプリが X を拒否する」という主張には、それぞれ個別の実行が必要です。また、すべてのパッチについて、同じパイプラインで作成し、以前と同じ失敗が続く対照が必要です。
→ references/long-task-discipline.md §single-variable discipline
R3 — 検証していない成果物を出荷したり、検証済みだと主張したりしないでください。
「組み立てられた」は「動く」と同じではありません。「プロセスが開始した」は「機能が動く」と同じではありません。「ログにエラーがない」は「チェックがなくなった」と同じではありません。インストールして起動し、変更した機能を実際に操作して、画面を確認してください。デバイスで実行中のビルドが自分の作成したものだと証明してください。ファイル名を信用せず、ハッシュを確認してください。
→ references/verification.md
R4 — パッチを適用する前に、該当する層を特定してください。実際の状況と食い違う場合は、分類し直してください。
広告、ペイウォール、機能ゲート、インテグリティチェック、アップデートゲートは、異なる層(Java、dex、ネイティブ、Dart/Unity、サーバー)に存在します。誤った層にパッチを適用すると、何も起きないか、アプリが壊れます。パッチが「効果なし」だった場合、診断が間違っています。パッチをさらに強めるのではなく、分類に戻ってください。
→ references/recon.md、次に症状インデックスで指定された層別ファイル
ツール選び — 適切なツールを使い、利用できないと判断する前に確認してください
この分野で無駄な試行錯誤の大半は、対象についての推論が誤っているからではありません。答えを出せないほど弱いツールに、適切な質問をしていることが原因です。インデックス化されたクエリを1回実行すれば済むのに、手動でエクスポートしたsmaliツリーをgrepで1時間調べたり、pip installですぐ使えるデコンパイラーがあるのにELFを手作業でたどったりします。弱い方法でも出力は得られるため、内側からは問題が見えません。
5つの義務。これは指示です。違反は好みの問題ではなく、不備です。
| 義務 | 実施すること | 無視した場合の代償 |
|---|---|---|
| エクスポートではなくインデクサーで全体を把握する | コードを読む前に、アーティファクトに質問する手段を整えます。droidasc findrefs / ddc findrefs(文字列/型/メソッドから、1秒未満ですべての参照箇所を検索)と、呼び出し元となるメソッド、数値定数を持つメソッド、デバッグ行テーブルなど、質問が結合または集計に関する場合はdexsql queryを使います。クラス一覧を取得するためのものではありません。デコンパイルで読めるのは、すでに場所を特定したクラスです。場所を特定する手段ではありません。jadxは最後の手段として使う読みやすいビューアーであり、信頼できる唯一の情報源でも、調査の出発点でもありません | 先に時間をかけてエクスポートしたものを対象に、質問のたびにgrepで何時間も費やすことになります |
| 不足しているツールをインストールする。それも作業の一部です | arm64用デコンパイラーがない場合、次の手順はインストールです。回避策を探す制約ではありません。インストールが本当に不可能な場合に限り、人に相談します | デコンパイラーなら数分で出せる結果を、手作業で何時間もかけて再導出することになります |
| その不足を補うために時間を使う前に、ギャップを明示する | 阻害要因を解消するために必要な機能と、このマシンにその機能があるかどうかをはっきりと述べます。これはG2の項目です | 10分でインストールできるツールを前提にせず、設計を進めることになります |
| スクリプトを書く前に、提供済みのスクリプトを使う | 解析、ハッシュ計算、アライメント、ホットプラグのプローブはすでに用意されています。scripts/dex_find_insn.pyの代わりに独自スクリプトを書くと、オフセットを計算せずに推測することになります | デコードは問題なくできても、誤った動作をするオフセットが得られます |
| 質問が扱うレイヤーに合ったツールを選ぶ | メモリ上のdexにはdex_mem_scan.py(検索と切り出し)に加えてdex_dump_validate.py(判定)が必要です。断片をデコンパイラーに渡すのではありません。呼び出すだけでよいアルゴリズムにはemulation-and-rpc.mdを使います。「このクラスは実行時にロードされるか」という質問には、発火するかどうかを確認するフックを使います | 重いツールがより安全とは限りません。もっともらしい答えが得られるため、答えが得られない場合よりも悪い結果になります |
利用できないと結論づける前に、python skills/apk-reverse/scripts/doctor.py --jsonを実行してください。
機能ごとの依存関係の充足状況、不足しているもの、インストールにかかるおおよその時間が報告されます。確認していない機能は、利用できない機能ではありません。**このツールは意図的にBLOCKEDを報告する場合があります。**JVMはあるもののzipalign/apksignerがないマシンでは再署名できません。ツールが存在するだけで機能があると推測せず、そのことをレポートに示します。詳細はreferences/toolchain.mdを参照してください。読み込むレジストリはscripts/capabilities.pyです。
対応範囲 — このスキルが主張することと、主張しないこと
このセクションが防ぐ失敗は、知識不足ではありません。対象向けに書かれていない、最も近い手順を適用して、自信満々に誤った回答を出すことです。 ほぼ当てはまる文書化済みの手法は、手法がまったくない場合より危険です。計画と用語、そして安心感を与える数値を伴うからです。
これらの主張の確度 — 見た目の問題ではなく、根幹に関わります。 このスキルのすべての主張には、次の3つのラベルのいずれかが付いています。あなた自身の結果にも、同じようにラベルを付けてください。
| ラベル | 意味 | 判断の根拠にしてよいか |
|---|---|---|
observed | コマンドが実行され、その出力が存在する | はい |
inferred | 観察結果から導かれるが、推論の段階が含まれる | 暫定的に |
unverified | 仮定したもの、または別の場所で報告されているが、ここでは再現していないもの | 検証対象としてのみ |
推測に代わる一覧: 検証済みの仕組みと、文書化済みだがおもに推論に基づく経路ごとに、何が対象で何が対象外かをまとめた一覧は、references/coverage-and-limits.md にあります。「このスキルで実際に X ができるか」という質問には、これを読み込んでください。
過剰に解釈しやすい部分的な対応範囲 — ここに挙げるのは文書化済みの経路ですが、依存要素はこのスキルには含まれていません。同じファイル内で、それぞれの境界を明示しています。Dart AOT の解析には、別途入手が必要なスナップショットダンプが必要です。また、フロントエンドの命名も重要です · 抽出シェルの復元は VMP の境界までで終わります · すべてのデバイスで Stalker のトレースが保証されるわけではありません · カーネル側の回答には、このスキルがひな型だけを提供するモジュールが必要です。
対象外 — その場しのぎで対応せず、対象外だと伝えてください: Unity / IL2CPP · React Native / Hermes / Cordova · iOS · サーバー側の権限判定の回避 · 既製のアンパッカーやアンチ検出のいたちごっこ · カーネルモジュールのビルドと配布。理由とエビデンスの境界は references/coverage-and-limits.md にあります。その記録に記載がないことは、対応の根拠にはなりません。
フォールバックの指示 — これは助言ではなく、規則です。 対象が対応範囲の一覧に合致しない場合、または症状インデックスに該当する行がない場合は、分岐を選ぶ前にいったん止まり、分類してください:13の質問に回答します(§まずはこちら)。それでも合致しない場合 — 不明なランタイムや、名前を特定できない仕組みなど — そのことを正確に伝え、それを特定できる最も低コストな実験を提案してください。 最も近い文書化済みの経路を選んで、そのまま適用してはいけません。ここで誤った分岐を選んでも、派手なエラーにはなりません。ビルドも実行もできるのに、誤った動作をする成果物ができあがります。
引き継ぎの境界 — このスキルが終わり、別の視点が始まるところ
気づかないうちに踏み込みやすい、4つの境界があります。助言の重複による食い違いを避けるため、同じ内容を繰り返すのではなく、もう一方の担当範囲を示しています。JNI 形式の表と、形式ごとの検証表は references/handoff-boundaries.md にあります。
- JNI — Java の
native宣言とその実装は、1つの関数を異なる視点から見たものです。このスキルでは、それぞれ異なるツールで読み解きます。動的登録では、シンボル検索は何も知らせずに失敗します。正式な説明:java2c-and-jni-sinking.md§JNI の境界 — シンボル検索が何も知らせずに失敗する理由。 - 強化処理 — dex 側のパッカーに関する観察結果は、ネイティブ側の実装に関する問題です。dex 側の識別は
packers.mdの担当であり、詳細な調査はネイティブ側で行います。 - ネイティブの異常 — APK 側から読み取ってください。判断すべきなのは、パッチが原因でプロセスが終了したかどうかだからです。シンボルの復元や OLLVM 関数のリバースは、異なるツールチェーンを使う別の作業です。
native-and-so.mdをその範囲まで拡張せず、別の担当範囲として案内してください。 - 成果物の形式 — 成果物が APK でなくなると、それに伴って検証の問いも変わります。G1 の他の3つの形式では、エビデンスがそれぞれ別の場所に移ります。しかも失敗は見過ごされやすく、特権的な結果が、ビルド完了を示す言葉で報告されます。
症状インデックス — 該当する行があれば作業を止めてください
ファイル名ではなく、症状からたどり着くこともあります。以下の各行は、すでに対処に時間がかかった失敗例です。観察している状況に該当する行が一つでもあれば、次のコマンドを実行する前にファイルを読み込んでください。次の三回の試行のあとではありません。この段階で第一原理から考え直すと、同じ時間を二度費やすことになります。ここにある項目の中には、答えがこのリポジトリで読まれずに残っている間に、手作業で再び導き出された教訓も複数あります。
| 観察された現象 | 最初に読む資料 |
|---|---|
再パッケージ/再署名したビルドがコード実行前にクラッシュする。SIGSEGV、全レジスターがゼロ、pc=0、fault addr が 0x0 付近 | native-tamper-and-suicide.md(意図的なクラッシュ)、次に code-virtualization-and-custom-linkers.md |
| パッカーなし(Application がアプリ本体のもので、dex は読み取り可能)なのに、やはりクラッシュする | code-virtualization-and-custom-linkers.md §ローダーの可能性は残っています。ただし、同じビルドが別の無関係な 2台目のデバイスでもクラッシュするなら、保護機構ではなく通常の起動時障害です |
パックの有無にかかわらず、すべてのデバイスで起動時にアプリが終了し、tombstone がない。crash_dump は already traced と表示し、logcat は exited cleanly (0) と表示する | バンドルされたクラッシュレポーターがシグナルハンドラーを取得しているため、プラットフォーム本来の記録は失われています。復旧手段は Frida の spawn-gating です |
Flutter アプリで、最初のフレームが完了する前に、メインスレッドで FORTIFY: pthread_mutex_lock called on a destroyed mutex による abort が発生する | dart-aot.md — libapp.so が実際に読み込まれているか確認してください。ネイティブのライフサイクル障害が表面化するのは、通常 Flutter エンジンのブートストラップです |
| ログではJava 層の署名/整合性チェックが成功しているのに、プロセスが終了する | code-virtualization-and-custom-linkers.md §Java 層の「署名キラー」はおとりです |
ライブラリを削除すると検証は通るが、UnsatisfiedLinkError: dlopen failed: library "X" not found が発生する | code-virtualization-and-custom-linkers.md §何時間も費やすデッドロック |
クラス全体が本体のない native 宣言として現れる | java2c-and-jni-sinking.md — メモリダンプの前に読んでください。これが Java2C なら、プロセスのどの時点にも見つかる DEX はありません。通常の dex に少数の native メソッドがあるだけなら、これは Java2C ではなく JNI シンキングです |
強化されたライブラリに対する Java_* 検索で何も見つからない | java2c-and-jni-sinking.md §JNI 境界 — シンボル検索が何も返さない理由 — 動的登録、または -fvisibility=hidden。有効な確認方法は「JNI_OnLoad をエクスポートし、Java_* はゼロ」です |
| 実際の作業内容を引用した証拠ファイル、トランスクリプト、README を公開しようとしている | references/desensitization-and-leak-scans.md — コミット前に scripts/scan_leaks.py を実行してください。ヒット一覧は確認すべき行の集合であり、誤った抑制は --show-exempt で確認できます |
| フッキングモジュールは実行されたように見えるのにログタグが出ず、「一度もロードされなかった」と記録しようとしている | references/precedents/logd-broken-module-never-ran-case-3.md — 壊れた logd は logcat に何も届けませんが、モジュールの実行全体は LSPosed のファイルログに残っています。両方の経路を確認してください |
| ライブラリの SONAME がファイル名と一致しない | code-virtualization-and-custom-linkers.md、native-and-so.md |
| 編集してもまったく効果がなく、エラーもない | server-config-and-updates.md §3(値がサーバーから送られている可能性があります)、次に packers.md §検証境界を特定する |
プロセスがクラッシュ記録なしにハングする、または uid 0 のキラーによって終了する | native-tamper-and-suicide.md §ルール(おそらく終了経路を戻らないようにしてしまっています) |
| 強化されたライブラリ内で、通常の null 参照外しのように終了する | native-tamper-and-suicide.md §意図的なクラッシュスタブ |
| アタッチ中/root 化中だけアプリが終了する | detection-and-anti-analysis.md。同じ条件で、まず未改変のオリジナルを実行してください |
resources.arsc/アラインメントについて言及され、[-124] でインストールに失敗する | repack-and-sign.md §2a — STORED かつ 4 バイト境界にアラインメントされていること。両方が必須です |
INSTALL_FAILED_* がなく、数値コード(例:[-99])だけでインストールに失敗する | repack-and-sign.md §ベンダーによるインストールの横取り — ビルドではなく、デバイス側のインターセプターです。root の pm install 経路を使用してください |
インストール後、am start が何もしない/スクリーンショットに別のアプリが表示される/am start -W がハングする | repack-and-sign.md §インストーラーがまだ画面を占有している可能性があります |
ログに Failure to verify dex file ...: Bad checksum と、通常のクラスに対する起動時の ClassNotFoundException が表示される | byte-level-patching.md §dex ヘッダーには 2 つの整合性フィールドがある — 順序が重要です |
| インストールは「成功」したのに何も変わらない、またはバージョンが更新されない | long-task-discipline.md §観察期間をクリーンに保つ |
| 証拠が互いに矛盾する、またはキャプチャに 2 つの状態が混在しているように見える | long-task-discipline.md §観察期間をクリーンに保つ |
| スクリーンショットを撮ったのに、ログやパッチ自体から結論を導き出した | long-task-discipline.md §確認していないキャプチャは証拠ではない |
| すでに結果を記録した実験を再実行しようとしている | long-task-discipline.md §長いコンテキストの劣化 |
| スクリプトが起動しない、またはツールが「見つからない」 | scripts/doctor.py、次に toolchain.md §「PATH にない」は「インストールされていない」ではない |
| フックまたはプローブでイベントがまったく報告されず、それを検出だと判断しようとしている | まず環境の自己報告を確認するために scripts/anti_detect_probe.js を使い、次に detection-and-anti-analysis.md §ステップ 3:チェック箇所の特定 — ステージ 0 からの検索順序を確認してください |
attach がハングしてから、プロセスがまだ ps に表示されている状態で失敗する | detection-and-anti-analysis.md §ステップ 3、ステージ 0 — /proc/<pid>/stat の D を確認し、対象を疑う前に、別の pid へのアタッチを 1 行の対照実験として試してください |
| tombstone、クラッシュ、ANR の記録なしにアプリが終了する | detection-and-anti-analysis.md §ステップ 3 — 正常な自己終了は、フックを用意する前にチェックが実行されたことを意味します。そこにある分岐条件で該当するステージを確認してください |
ダンプ領域が誤った内容として検証される、または base.apk の r--s ビューが dex のように見える | advanced-unpacking.md §この方法でできないことと、時間を費やす前に見分ける方法 |
| 特定機能のネットワーク障害(ログイン/登録/支払い)が起きる一方、ほかは動作する | tls-and-cert.md — パッチが原因だと決めつけないでください |
| すべて動作するが、再パッケージ後にすべての署名付きリクエストが失敗する | signature-derived-keys.md |
再署名したビルドは正常に動作し、UI 全体を描画し、エラーも記録されないのに、1 つの機能だけが読み込まれず、dumpsys/DNS/logcat にその機能へのリクエストがまったく見当たらない(リクエストが拒否されたのではなく、リクエスト自体がない) | code-virtualization-and-custom-linkers.md §ネイティブチェックが実際に読み取るもの — リクエストが構築される前に拒否されています。上の行とは別です。「送信されて拒否」と「送信されていない」では、担当箇所が異なります |
| 機能が見つからない原因がパッチか、対象本来の動作か判断できない | long-task-discipline.md §単一変数の原則。変更ゼロの対照実験を同じパイプラインで実行し、決定的な検証として、パッチをメモリ内だけで適用した未改変のオリジナルを、同じデバイス、同じネットワークで実行してください |
Frida の spawn では UI が表示されず、mCurrentFocus が null のまま、スクリーンショットが真っ白になり、Activity スタックが構築されない | dynamic-frida.md §spawn では Activity スタックが起動しない:パッチをメモリに書き込み、デタッチしてから、通常どおり Activity を起動してください |
実験中に frida-server が何度も消える、または作業中にデバイスが再起動する | dynamic-frida.md §ROM がインストルメンテーションを狩るとき |
| 広告を停止するはずのパッチを適用した後も広告が表示される | server-config-and-updates.md §6(キャッシュされた設定/リモートでの再有効化)、次に ad-removal.md §ステップ 4(SDK 自身のログ行数を数える。n → 0 であり、「見えなかった」ではありません) |
| 強制更新または「更新必須」のゲートによってビルドがブロックされる | updates-and-forced-upgrade.md §ステップ 6 |
| ダイアログは消えたのに、機能はまだロックされている | membership-and-limits.md/account-gates.md — 再度パッチを適用する前に、サーバーとクライアントのどちらが権限を持つか判断してください |
| 経路を「ブロックされた」として破棄しようとしている | packers.md — 経路を除外する前に読み直してください。原因の誤認によって、実行可能な経路が何時間も失われた事例があります |
| 作業が長引き、何がすでに証明されているか分からなくなった | long-task-discipline.md §記録を常に更新する |
ダンプされた dex を完全に解析でき、クラスもすべて揃っているのに、メソッド本体の多くが return-void スタブまたは nop 埋めになっている | references/advanced-unpacking.md — 抽出シェルです。内容を信頼する前に scripts/dex_dump_validate.py で stub% を測定してください。メソッド本体の復元は別の経路であることも把握しておいてください |
frida のダンプが書き込み中に停止する(script has been destroyed)、またはダンプ対象のプロセスの pid が変わり続ける | advanced-unpacking.md §frida が拒否されたときのダンプ — まずメモリ圧迫を除外してください。メモリー回収後の再起動ならインストルメンテーションは不要です |
| 再パッケージが複数の独立したチェックで拒否される、またはストア更新後もビルドを動作させ続ける必要がある | references/lsposed-and-modules.md — APK ではなくモジュールを配布してください。G1 の形式表で該当する場合を確認できます |
| フックモジュールをインストール、有効化、スコープ設定したのにログタグが表示されず、一度も実行されなかったと判断しようとしている | lsposed-and-modules.md §デプロイ、有効化、検証 — ここでは logcat だけに基づく判断がすでに誤っていました。ある ROM では logd が壊れており、出力は /data/adb/lspd/log/modules_<ts>.log にしか届きません |
| モジュールのエントリークラスが自身の dex に存在せず、ロードされることはないのに、パッケージはインストール、有効化され、正常に見える | references/lsposed-and-modules.md — assets/xposed_init と dex 内の実際のクラスを照合してください。インストールされたことは何の証拠にもなりません |
| アプリを変更するのではなく、対象自身のルーチン(署名、トークン、暗号化)を呼び出すだけでよい | references/emulation-and-rpc.md — エミュレートするか、Frida RPC 経由で実行中の関数をサービス化してください |
ネイティブ関数が数千行に及ぶ switch 状態機械になっている、または逆コンパイラーの出力が意味をなさない | references/native-dbi-and-deobfuscation.md — OLLVM の形状、Stalker トレース、トレースで実際にどこまで分かるか |
Stalker.follow は設定できるがイベントが届かない、または頻繁に呼ばれる libc エクスポートを追跡するとプロセスがクラッシュする | references/native-dbi-and-deobfuscation.md §6 障害モード — このリポジトリで両方を測定しています |
コード変更なし、注入スレッドなし、かつ TracerPid = 0 の状態で対象を観察または変更する必要があり、ページチェックサムがすべてのフックを検出する | hardware-watchpoints.md — ptrace 経由のハードウェアデバッグスロットと、実態に即したステルス性の記録。その後、トレーサーがまったく不要なカーネル側の経路について kernel-event-injection.md を確認してください |
| クラッシュのバックトレースを信頼できない — 誤ったモジュールや匿名領域にあるフレーム、平坦化/仮想化されたコード、または破損したスタック | hardware-sampling-and-crash.md — 実際の制御フローを示す BRBE 分岐記録/ETE テール。次に scripts/trace_addr2sym.py ですべての PC をシンボル化してください |
| 実行時アドレス(ヒット、トレース PC、分岐点)の一覧があり、16 進数ではなくシンボル/静的オフセットが必要 | hardware-sampling-and-crash.md §3 クラッシュループ — vaddr/ファイルオフセットの規則に従って scripts/trace_addr2sym.py を使用してください |
| 仮想化関数のトレースがインタープリターループの繰り返しにすぎず、ノイズのように見える | scripts/vm_handler_slice.py — ディスパッチャーのエントリーでハンドラーセグメントに分割し、シグネチャをクラスタリングして、ゲストオペコードの軌跡を復元します(xvmp で調整済み) |
| 実行された内容だけでなく、同じ操作(パスワード/ライセンス/キー)の 2 回の実行がどこで分岐するかを知りたい | ete-diff-tracing.md — ETE ハードウェアトレースのゲートと差分解析手法。scripts/trace_diff.py では Stalker ログも差分比較できます |
| 生成された KPM/LKM テンプレートを手元に持っている、またはカーネル経路を使う計画がある | kernel-event-injection.md — プリミティブの一覧、組み合わせの手順、カーネル側の各ファイルが攻撃手段になる前に通過すべきゲート |
| 通信が protobuf/gRPC/QUIC である、またはプロキシで TLS は確認できるのに Flutter アプリのリクエストが失敗する | references/protocol-reverse.md — スキーマのない protobuf、フレームキャプチャ、ネイティブ側のピンニング |
ユーザー空間のフックを適用してもアプリが終了する。チェックが raw svc 経由で /proc/self/status を読み取るか、JNI_OnLoad より前に実行される | references/kernel-and-environment-hardening.md — 上下の次のレイヤーで実際にできることと、調査を切り上げるタイミング |
| スマートフォン本体から APK を編集、再パッケージ、署名、または検査する必要がある | references/on-device-tooling.md、scripts/mt_mcp_probe.py |
| キャプチャした本文をデコードしても読める内容が得られない、または長さ付きフィールドが文字列、ネストしたメッセージ、パック済み配列のどれか判断できない | protocol-reverse.md。ワイヤー上の protobuf(実測) — scripts/protobuf_decode_raw.py を実行してください。候補一覧と tie: 行が答えです |
| メソッド本体は存在するのに独自オペコードとしてデコードされ、その理由ではなく対応表が必要 | vmp-differential-analysis.md、次に形状の診断について advanced-unpacking.md |
ストアのビルドが base.apk と split_config.*.apk として届く、または再ビルドした一式の各ファイルは個別に検証できるのに、セットとして拒否される | split-apk.md — pm install-multiple ではすべての構成ファイルに同じキーストアを使用してください。また、結合した単一 APK を信頼する前に、結合が許可されるか確認してください |
ゲート — パッチを当てる前に、順番にクリアする
各ゲートは、合格基準を伴うアクションです。クリアしていないゲートを越えて先に進んではいけません。「考え方は理解した」ことをクリアとみなしてはいけません。ゲートを飛ばすのは近道ではありません。作業をやり直すことになる道です。
G1 · 成果物の形式 — およびそのコスト上限。 最終的にどのような成果物が存在すべきか、どのような制約があるか(root化が必要か、標準状態のデバイスにインストールできる必要があるか、アップデート後も動作する必要があるか、オンラインかオフラインか)を、誰かにそのまま伝えられる一文で述べてください。合格: その一文に、作業内容ではなく、検証可能な制約が含まれています。不合格: 成果物が使われることのない環境で問題を解決しようとしています。
次に、その一文が示す形式を選んでください。「再構築した自己完結型 APK」は4つの選択肢のうちの1つにすぎません。この選択をするのは、再パッケージに失敗した後ではありません。
| 形式 | 適切なケース | 必要なコスト |
|---|---|---|
| 再構築してインストール可能な APK | クライアントが動作を担っており、複数箇所での整合性チェックがなく、抽出シェルもない場合 | 再パッケージ、再署名、および検証用のデバイス |
| LSPosed / Xposed モジュール | ロジックはクライアント側にあるものの、アプリが再パッケージに対抗する場合(複数箇所での署名チェック、シェルによる自己検証など)、または管理下のroot化デバイスでのみ動作すればよい場合 | root化デバイス、モジュールの土台、およびフックフレームワークを検出してはならないアプリ → references/lsposed-and-modules.md |
| ローカル RPC / エミュレーションサービス | アプリを変更する必要はなく、署名ルーチン、トークン、暗号化関数などの呼び出しが必要な場合 | 稼働中のデバイス、またはエミュレートしたローダーと呼び出し用ハーネス → references/emulation-and-rpc.md |
| 境界を明示した解析レポート | 権限がサーバー側にある場合、または対象が本格的な VMP / 抽出シェルで、復旧コストが作業の価値を上回る場合 | 出荷するものはありません。それが正直な答えであり、失敗ではありません → references/advanced-unpacking.md、references/server-api.md |
最初の列から切り替える判断基準: 証拠から (a) すべて通過させる必要がある独立した整合性チェックが複数ある、(b) メソッド本体が呼び出し時にしか存在しない抽出シェルがある、または (c) プライベートオペコードとしてデコードされる本体がある、と分かった時点で、「再構築した APK」の選択をやめてください。その時点で再パッケージの手法は、単にコストが高いのではなく、実行不可能です。成果物を述べる文には、代わりに選んだ形式を明記してください。ここで選んだ形式を各チェックポイントで読み返すことが、このスキルで最もコストの高い軌道逸脱を防ぐ手段です(references/long-task-discipline.md)。
G2 · 環境の実態と機能の棚卸し。 scripts/doctor.py を実行してください(デバイスを使う場合は scripts/preflight.py も実行してください)。合格: この環境で実際に実行できるツールチェーンとスクリプトが分かり、環境に関する警告(時計のずれ、残存する adb forward / プロキシ、すでに実行中のデバイス側 frida プロセス、PATH 外にインストールされたツールなど)を確認し、この対象に必要な機能と、このマシンが持つ機能を比較して書き出しています。 タスクでほぼ確実に必要となる2~3のレイヤー(たとえば「arm64 ネイティブコードの逆コンパイル」「Dart AOT スナップショットのダンプ」「デバイス側 TLS の検査」「dex 全体の相互参照」)を挙げ、それぞれを利用可能/不足しているがインストール可能/実質的に利用不可能に分類してください。不合格: 自分の環境が原因の失敗を対象のせいにしようとしている、または10分でインストールできるツールを回避するために1日費やそうとしています。ツールが不足しているレイヤーはタスク項目であり、それを回避するよう設計するための制約ではありません。→ references/toolchain.md §機能不足の解消
G3 · コードの場所。 マニフェストと dex から、パッカーが存在するか、アプリ独自のコードがどこにあるか(dex/ネイティブ/Dart/Unity/サーバー)、またその一部がネイティブに仮想化されているかを回答してください。合格: 変更する予定の動作を担うクラスを特定できるか、見つけるための具体的な計画があること。不合格: まだ特定していないレイヤーにパッチを当てようとしていること。調査で「パッカーなし」と判断した場合も、仮想化の形態を確認してください。上記のインデックス行を参照してください。
G4 · ベースラインと対照。 合格: 対照実行(変更していないオリジナル、または同じパイプラインで変更を加えずに再パッケージしたもの)があり、観測された障害を記録していること(停止する場合は停止までの時間も含む)。不合格: パッチ適用後のビルドに不具合が起きたとき、比較対象がなく、それ以降の測定結果すべてが反証不可能になること。
まずはここから:13の質問で対象を分類する
ツールに触れる前に、13の質問すべてに順番に回答してください。回答を誤っても明白なエラーにはなりません。ビルドも実行もできるのに、意図しない動作をする成果物ができてしまいます。 回答欄には、実行する操作と、その詳細を記載したファイルの両方が示されています。質問に取りかかる前に、そのファイルを読み込んでください。
| # | 質問 | 回答によって変わること | 読み込む項目 |
|---|---|---|---|
| 1 | アプリはパックされている/強化されているか? マニフェストの application android:name を確認します | アプリ独自の Application ではなく、サードパーティ製のシェルクラスが指定されている場合はパッカーが使われています。まずその処理に対応する必要があります | recon.md |
| 2 | 実際に動作を担っているのはどこか? — 広告 SDK · クライアント側で UI を描画するためにサーバーから発行される設定 · メンバーシップ/VIP · 機能フラグまたはデバッグスイッチ · API レスポンスで決まるものなど | クライアント側にあり削除できるもの、クライアントが判断に使うサーバー提供データ、クライアント側のパッチでは見た目しか変わらないサーバー側で決定されるものを見分けます。早い段階で判断してください。存在しない広告 SDK を探すと何時間も無駄になり、サーバー設定の形式の場合は探すべき SDK がありません | ad-removal.md、server-config-and-updates.md、membership-and-limits.md、server-api.md |
| 3 | アプリ独自のコードは通常の dex にあるか、それともネイティブ/Flutter/Unity に移されているか? | タスク全体で使うツールチェーンが決まります。決める前に、手早くランタイムで確認してください。対象の UI に関係する明らかな Java クラスにフックを設定して、その UI を再現します。フックが発火すれば Java がその UI を担っています。UI が明らかに表示されているのにフックが一度も発火しない場合は、ランタイムまたはネイティブコードが描画しており、dex だけを対象にした計画では行き詰まります。発火がゼロなら、dex の調査を続けないでください。このスキルの履歴で最も高くついた誤った方向転換です | recon.md §アプリ独自のコードはどこにあるか、framework-runtimes.md |
| 4 | 成果物にどのような機能が必要か? テスト可能な一文で書き、各チェックポイントで読み返してください | このスキルで最も高くつくのは、要件からのずれです。ランタイムだけでの結果(データ編集、ライブフック、ホスト名のブロック、ホストプロキシ)が成功に見えても、要件を満たさないことがあります。判断軸は、権限(ルート化されていないか?)、変更形態(再構築した成果物か、ライブ計測か)、ABI/デバイスクラス、ネットワーク、永続性、配布形態(単体で完結するか?)です。深い抽出シェル、VMP、または独立した整合性チェックが複数あることがすでに証拠で示されている場合は、G1 の 4 つの形式に照らして再検討してください。再パッケージの列は、コストが高いのではなく、実行不可能な可能性があります | long-task-discipline.md §最も高くつくずれ |
| 5 | アプリは独自の署名を検証するか、それともサーバーが検証するか? | アプリ側で検証する場合は回避が必要です。サーバー側で検証する場合は、再署名すると後でアプリが気づかないうちに動かなくなります | repack-and-sign.md、server-api.md |
| 6 | デバイスの状況は? — ルート化済みの実機、ルート化済みのエミュレーター、またはデバイスなし | Frida がそもそも使えるか、そして成果物を実際に使う環境でテストできるかが決まります。最初の実験の前に scripts/preflight.py を実行し、予想外の失敗が起きるたびにもう一度実行してください。デバイスの状態、停止したデバイスサーバー、残っているプロキシ、時刻のずれは、いずれもパッチの不具合に見えることがあります | environment.md、pitfalls.md P9 |
| 7 | 実際に実行されているアーキテクチャはどれか? | getprop が示すのはデバイスが申告する値で、primaryCpuAbi が示すのはパッケージマネージャーが選んだ値です。どちらも実際に実行されるものではありません。真の根拠となるのは、ライブマッピングだけです。マッピングされていないライブラリ、使用中のトランスレーター、想定と異なる ABI は、どんなパッチよりも大きく計画を変えます | native-and-so.md §アーキテクチャ間の違い、scripts/lib_map.py |
| 8 | ほかは動作しているのに、実行時に失敗する特定の機能(ログイン、登録、支払い、API に依存する画面)があるか? | 特定機能に限られたネットワーク障害は、多くの場合、パッチの問題ではなく、特定のコードパスにおける TLS/証明書の問題です。アプリが独立した信頼チェーンを 2 つ持つこともあるため、「ほかのリクエストは動作する」ことは何の証明にもなりません。署名チェックの調査を始める前に、数分でこの可能性を除外してください | tls-and-cert.md |
| 9 | 入力は自分で作成していないビルド(流通している「クラック済み」/「改造済み」APK)だったか? | そのようなビルドは再保護されていることが多く、元のビルドよりも多くのレイヤーが含まれる場合もあります。また、注入されたコンポーネントやエンドポイントが含まれていることがあります。パッチ作業の土台として絶対に使わないでください | third-party-builds.md |
| 10 | クライアントは独自の署名証明書を使ってリクエストに署名するか? | 最初の再パッケージの前に toCharsString()/signatures[0]/getPackageInfo(..., 64) を grep してください。その値がネイティブの HMAC/DES ルーチンに渡される場合は、再構築した APK で、読み取り箇所すべてに元の証明書値をハードコードする必要があります。そうしないと、アプリは起動して正常に見える一方で、署名付きリクエストがすべて失敗します。再パッケージで最も高くつく、表面化しにくい失敗です。15 分の grep で防げます | signature-derived-keys.md |
| 11 | アプリがしばらくすると勝手に終了するか — Java スタックがない、またはバグのように見えるネイティブクラッシュが起きるか? | 強化されたライブラリが kill を呼び出すことはまれです。多くの場合、障害を意図的に発生させるよう仕向けます(小さな定数を読み込み、それをポインターとして使うなど)。そのため、終了が通常の欠陥のように見えます。手を加える前に守るべきルールは 2 つあります。実際に発生する仕組みを特定すること(シグナルと tombstone で切り分けます)。そして、戻らなくするのではなく、戻るように無効化することです。スタブをスピンさせるとプロセスがフリーズし、原因とは似ても似つかない症状が発生します | native-tamper-and-suicide.md |
| 12 | このビルドは 1 週間後も使えるか? | バージョンチェック、アップグレードのプロンプト、自己更新の経路がある場合、パッチを適用していないビルドは停止されたり、リモートから置き換えられたりする可能性があります。これは編集 1、2 回で対応でき、作業が長持ちするかどうかを左右します。後回しではなく、ビルドの一部です。バージョン変更なしで削除した動作を復元するホットアップデート/リモート設定の経路も確認してください | updates-and-forced-upgrade.md |
| 13 | リクエストはサインインや電話番号の紐付けに関係するか(「ログイン不要」、「紐付けをスキップ」、「ゲストでも可」) | クライアント側のゲート(パッチ可能)と、アカウントに紐付いたリソース(サーバーが応答するアカウントがないため画面が空になる。パッチ不可)を区別できます。まず分類してください。また、ゲートを通過するためにセッションを決して捏造しないでください。サインアウト状態より悪い状態になります | account-gates.md |
| **長時間タスクのルール(助言ではなく条件です):**時間がかかりそうな場合は、ただちに | |||
references/long-task-discipline.md を開き、作業を進める間、記録を更新し続けてください。実験を始める前に、「反証された結論」と「行き止まり」の各セクションを読み直してください。これまでの調査結果を失うことは、このスキルにおいて最も大きな損失であり、完全に防ぐことができます。 |
最初から最後までのワークフロー
手順には順序があります。明記されたスキップ条件を満たす場合に限り、手順をスキップしてください。「不要そうに思える」は条件ではありません。pitfalls.md に記載された失敗の大半は、これが原因で起きました。
2回失敗したら中止するルール。 同じ種類の試みが2回失敗したら、中止して分類に戻ってください。すでに2回失敗した仮説を、3回目に条件を変えて試してはいけません。同じパターンで2回失敗したなら、調整すべきなのはパラメーターではなく、モデルのほうが間違っています。3回目の試みでは、最初の2回ですでにわかっていたことを確認するために、ラウンド全体を費やすことになります。その時点で、症状インデックスを読み直してください。まさにこの場面のために用意されています。
- 事前確認を行い、その後に偵察する —
scripts/doctor.pyは、最初に実行するコマンドとして最も手軽です。この環境で実際に動作するツールチェーンとスクリプトを報告し、実験を台無しにする環境上の事実(時計のずれ、残存するadb forward/プロキシ、デバイス側で既に実行中の frida プロセス、PATH の外にインストールされたツールなど)を明らかにします。デバイスが関係する場合は、他の作業に取りかかる前にscripts/preflight.pyを実行します(数秒で済み、誤った結論につながる多くの要因を防げます)。その後、references/recon.mdを確認します。マニフェスト、パッケージ名、バージョン、ABI、dex の数、パッカー、組み込み SDK、アプリ独自のコードがある場所を調べます。ここでの10分が、後の数時間を節約します。パッカーが使われている場合は、他の作業に取りかかる前に展開します(references/recon.md§unpacking)。読み取れないコードにはパッチを適用できません。また、暗号化されたペイロードの長さから、ダンプした dex のどれがオリジナルかを判断できます。メモリダンプは、信頼する前にハッシュで重複を除去し、構造を検証する必要があります。 偵察でパッカーが見つからなかったのに、再署名したビルドが停止する場合は、references/code-virtualization-and-custom-linkers.mdが扱うレイヤーが原因です。「パッカーなし」は「編集可能」を意味すると決めつけて、作業を進めないでください。 アプリが何もしなくても停止する場合 — 特に起動後ほぼ一定の時間で停止する場合や、ネイティブクラッシュが発生する場合 — パッチを計画する前に、その仕組みを特定します(references/native-tamper-and-suicide.md、scripts/native_crash.py)。停止までにかかった時間を記録してください。これは後のすべての試行を比較する基準となります。これがなければ、実行が維持されたのか、停止スケジュールが変わったのかを判断できません。 スキップ条件: スキップ不可。§Gates の G2/G3 は、ここでクリアするか、まったくクリアできないかのいずれかです。 - 文字列とエンドポイントを抽出する — dex の文字列テーブルから、アプリの API サーフェスと SDK の一覧を把握します。デコンパイラーは不要で、処理も高速です。スクリプト:
scripts/dex_strings.py。 - 処理を担うクラスをたどる — 挙動をラップするクラスを見つけます(アプリはほぼ必ず、サードパーティ SDK を1つのヘルパーでラップしています)。逆引きの手順:
references/dex-patching.md§finding-the-call-site。 - パッチを適用するレイヤーを決める — クライアント SDK 呼び出し/クライアントのレンダリング/クライアントのデータ消費/サーバーとの契約。
references/ad-removal.mdの表を参照してください。 - 的を絞ってパッチを適用する —
references/dex-patching.mdとreferences/byte-level-patching.mdを参照してください。 手法は2つあります。どちらを選ぶかは好みではなく、判断が必要です。 変更が既存の命令スロットまたは定数に収まる場合は、同じ長さのバイト編集(scripts/dex_patch_bytes.py。scripts/dex_find_insn.pyで位置を特定)を使います。何も移動しないため、オフセット、try/catch ブロック、デバッグポインターが無効になりません。新しい命令が本当に必要な場合に限り、dexlib2 によるメソッドの書き換え(scripts/dexpatch/)を使います。smali のツリー全体を往復させると、R8 で最適化された dex に損傷を与え、その影響が実行時にしか現れないことがあります。メソッドの再構築によってファイルサイズも増加します(実測値:あるサンプルではdebug_infoが924 Bから22.8 KBに、dex が4.32 MBから7.73 MBに増加)。どちらの方法を使う場合も、dex ヘッダーの整合性フィールドを再計算します(署名が先、チェックサムが最後)。references/byte-level-patching.md§the dex header has two integrity fields を参照してください。 - 再パッケージ化して署名する —
references/repack-and-sign.mdを参照してください。META-INF/全体を削除しないでください。このたった1つのミスで、他は正しいビルドが台無しになります。 6b. ビルドを完了とする前に、更新経路を無効化する。 アプリが更新を確認する場合は、2層のパッチを適用します(references/updates-and-forced-upgrade.md)。更新ルーチンのエントリを no-op にし、バージョン比較を「更新なし」側に強制します。リモートから無効化または置き換えられるビルドは納品物になりません。後で再ビルドするより、今ここで対処するほうが数分で済みます。削除した挙動を復元できるリモート設定またはホットアップデートの経路についても、同様に対処します。 6c. アカウント関連のゲートは、分類してから扱う — 依頼にサインインまたはアカウント連携が含まれる場合は、references/account-gates.mdを適用し、利用可能になる保護対象画面と、コンテンツがアカウントに紐づいているため空のままになる画面を明確に説明します。 - デバイスで検証する —
references/environment.mdとreferences/verification.mdを参照してください。次の点を確認します。起動すること、変更対象の挙動が実際に変わっていること、無関係な機能が壊れていないこと、そしてブロッキングダイアログなしでアプリが通常の UI に到達すること。まず、デバイス上で実際に成果物が変更されたことを証明します。パッケージマネージャーが成功を報告しても、介在する確認画面で承認されたとは限りません(P18)。起動後最初の約20秒は継続してキャプチャし、そのキャプチャを確認してください。サンプリング間隔があるとブロッキングモーダルを見逃すことがあり(P20)、確認していない画像が大量にあっても証拠にはなりません。アクセシビリティツリーが空の場合、画像は代替手段ではなく主要な証拠です。 - 学んだことを記録する — 失敗によって30分を超える時間を費やした場合は、
references/pitfalls.mdに追加します。このファイルは、このスキルにおいて最も価値のある成果物です。
「完了」の意味 — それより早く主張してはいけません
完了を報告する前に、以下のすべての項目が真でなければなりません。それに満たないものはチェックポイントであり、残っている作業とともに、口に出してチェックポイントとして明示しなければなりません。早すぎる「完了」は、報告しうる中で最も有害なものです。なぜなら、ユーザーが問題は解決したと信じているうちに、調査を終わらせてしまうからです。
- 成果物が存在し、その同一性が記録されている — ファイル名ではなく、パスとハッシュです。
- 成果物の文言が名指しする環境でインストールされ、起動された(G1/R1)。その環境があなたにとって利用可能でなかった場合は、その旨を述べ、結果をそれに応じてラベル付けしてください。
- あなたが変更した挙動が変更されたと検証されている — エラーメッセージの不在によってではなく、その機能の直接的な観察によって。「ログはクリーンだ」は証拠ではありません。「画面に X が表示されている」が証拠です。
- それが触れる機能が依然として動作する。 あなたはそれらを実際に動かしました。起動するが、影響を受ける機能が死んでいるビルドは結果ではありません。
- 元の制約が残っている場合はそれが述べられている — それを引き起こす結合とともに。そうすれば次の担当者がそれを受け入れるかどうかを判断できます。
- あなたが行ったことのいずれも、それが目標であった場合を除き、ターゲットやデバイスを壊れた状態のままにしない。また、特権を要する回避策は、成果物ではなくフォールバックとしてラベル付けされます。
項目 1〜4 が成り立つのに環境が間違っていた場合、あなたが持っているのはプロトタイプであり、成果物ではありません。「プロトタイプ」と述べ、ギャップを名指ししてください。
停止条件 — 再試行せず、停止して再分類する
これらは、前進を押し続けることが誤った動きとなる瞬間です。それぞれがどこかで何時間ものコストを生んでいます。
- 同じ形の試みが二度失敗した。 上記のツーストライクルールを参照してください。
- あるパッチが効果を持たず、あなたがその第三の変種を試そうとしていた。 効果がないとは、パッチが運悪く外れたのではなく、診断が誤っていたということです。層を再分類してください。
- 新しい失敗があなたの現在のモデルに位置を持たない。 これが症状インデックスのトリガー条件です。
- あなたが、そのブロックがあなたのパイプラインのものではなくアプリのものだことを証明する対照ビルドなしに、あるルートを「ブロックされた」と書き捨てようとしている。 誤って帰属されたブロックは、実行可能なルートを消し去ってきました。
- あなたがエラーの不在をもって成功を主張しようとしている。 §「完了」の意味を参照してください。
- ある測定値が、あなたがすでに確定として記録した結論と一致しない。 結論を再び開いてください。測定値を説明で片付けないでください。
絶対に守る制約
違反した場合、これは好みではなく欠陥です。 各行には違反を検出するチェックを記載しているため、「遵守したか」は自分で判断するのではなく、実行可能なコマンドで確認できます。
| 制約 | 絶対に守るべき理由 | チェック |
|---|---|---|
| 入力は読み取り専用です。 コピー上で作業し、正常なベースラインを保持してください | 元ファイルを編集すると、差分を取るための唯一の参照元が失われます | 最初の編集前に元ファイルのハッシュをgitで確認します。エントリーレベルの証明にはscripts/apk_diff.pyを使います |
| 一度に変更する変数は1つだけにし、コントロールビルド(同じパイプラインを使い、パッチはゼロ)を行います | 複数の変更をまとめて失敗しても何も学べず、原因を誤って特定することになります | コントロール実行があり、その結果が記録されています |
| dexを編集するたびに構造を検証してください | パッチがアセンブルできても、ロード時にクラッシュすれば、パッチが成功したように見えることがあります | scripts/dex_classdiff.pyを使い、変更する意図のないクラスについて、クラスセットとアクセスフラグに差分がないことを確認します |
| 広く共有されているメソッドにはパッチを当てないでください。まず呼び出し元を数えてください | Long.valueOfのようなヘルパーが30か所から呼び出されているなら、広告専用のフックではありません | 変更しようとしているメソッドに対してscripts/find_refs.pyを実行します |
| UI要素を抑制するためにAPIを失敗させないでください | エンドポイントの他の機能が依存する404/400が発生すると、画面全体が巻き添えになります。ビルドが起動画面から先に進まないことで確認できます | データの消費層または描画層で抑制します。リクエストのパスが変わったなら、それが問題です |
| ネイティブの終了経路を無効化するときは、戻らせないのではなく、戻るようにしてください | スピンするスタブはロックを保持したまま呼び出し元をフリーズさせます。無関係なスレッドも停止し、ハング、外部からの強制終了、再起動ループなど、原因とはまったく異なる症状が現れます。クラッシュ記録も残りません | 無害な値を返し、失敗を示す-1より成功を示す0を優先します。pthread_exit/exit/abort/snprintf/closedirには触れないでください。native-tamper-and-suicide.md |
| 結論を出す前に観察し、待っている間も観察してください | 実際に画面を見れば、座標を推測して5回試すよりも1回で答えが得られます | キャプチャして内容を確認します。同一バイト列のサンプルでは、何も変わらないことを意味します。scripts/snap.py、environment.md §画面を見る |
| すべてのコマンドに制限時間を設け、その時間は計測結果をもとに決めてください | 制限のない呼び出しでは、停止状態と「まだ処理中」の状態を区別できず、進行中と誤認します | 操作にかかる時間を一度計測して記録し、その値から期限を設定します。long-task-discipline.md §待機時間に上限を設ける |
すべての主張にobserved、inferred、unverifiedのいずれかのラベルを付けてください | 「おそらく」「そうなるはず」「理論上」は根拠のある調査結果ではなく、ラベルのない推測は事実として広まります | observedにはコマンドとその出力が必要です。それ以外を確定した事実として記述してはいけません |
| 「完了」とは、ユーザーに見える結果が得られたことを意味します | 画面にブロックするダイアログが残っているなら、ログがどれほどきれいでもタスクは完了していません | §「完了」の意味にある6項目すべてを満たします。ログ行がないことは成功の証拠にはなりません |
| 複数の根拠を合わせて経路を断念しないでください | 2つの変更を同時に行った後に失敗した場合、原因の特定は仮説にすぎません。誤った原因特定によって、有効な経路が何時間も失われることがあります | 断念を記録する前に、変更を1つずつにして再実行します |
| 何かを計測する前に、デバイス上で変更が反映されたことを証明してください | インストール成功が示すのはリクエストの成功であり、ディスク上のアプリではありません | デバイス上の成果物とビルドのハッシュを照合します。そうしなければ、その後のすべての観察は以前のビルドについてのものになります |
| 再度パッチを当てる前に、失敗が発生した層を正しく特定してください | 特定機能に限定されたネットワーク障害は、アプリ独自のTLS/証明書の問題であることがよくあります。存在しない署名チェックを追いかけると何時間も無駄になります | まず、同じデバイスとネットワークで変更していない元のアプリを実行します(tls-and-cert.md) |
リファレンス索引とスクリプト索引
両方の表は references/routing.md にあります。ここを一度読み込めば、読み込むタイミングを含むすべてのリファレンスファイルと、処理内容を含むすべてのスクリプトを確認できます。これらをここに重複して載せていないのは意図的です。このファイルはスキルが有効になるたびに全体が読み込まれますが、約140行ある2つの表の検索データは、何をするかをすでに決めるまでは必要ないためです。
上記の症状索引はそのまま残しています。症状からファイルまでは1ホップでたどれる必要があるからです。何かが失敗したときに選ぶのはファイルではなく、失敗を認識することです。その時点で2ホップの検索が必要になると、まさにその手順によって停止シグナルが見落とされます。
references/evidence-summary.md は、インストール済みのコピーから主張の強さに関する疑問に答える唯一のリファレンスです。機能 → 1行の結論 → observed/inferred/unverified → スキルに同梱された証拠、という構成です。主張の強さによって信頼するかどうかが決まり、実行記録が手元にない場合に読み込んでください。
python check_routing.py は、このファイルと references/routing.md の内容が引き続き一致していること、すべてのリファレンスファイルがそこに記載されていること、すべてのスクリプトがそこに記載されていることを確認します。CI で実行されます。