mcpField
R
re-hybrid-app

re-hybrid-app

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

re-hybrid-app について

re-hybrid-appは、FlutterとReact Nativeアプリを調べるリバースエンジニアリングスキルです。アプリのロジック復元やJavaScriptとネイティブコード間の通信分析に向け、静的解析と実行時観察でブリッジ上のデータ交換を特定します。

主な機能

  1. 01FlutterとReact Nativeアプリの識別
  2. 02DartスナップショットとJSバンドルの復元
  3. 03Hermesバイトコードの解析
  4. 04JavaScriptとネイティブ間の呼び出し特定
  5. 05プラットフォームチャネルの実行時観察

活用シーン

  1. 01FlutterアプリのDartロジック復元
  2. 02React NativeのHermesバンドル逆アセンブル
  3. 03MethodChannelやNativeModules経由の追跡
  4. 04ハイブリッドアプリの機密データ交換の調査

インストール

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

$npx skills add https://github.com/dslsdzc/rev-skills --skill re-hybrid-app

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

詳細

ハイブリッドアプリのリバースエンジニアリング(Flutter / React Native)

使用する場合 / 使用しない場合

  • 使用する場合:アプリ内で libflutter.so / libapp.so(Flutter)または assets/index.android.bundle(React Native)が見つかった場合
  • 使用する場合:ハイブリッドフレームワークのビジネスロジック(Dart スナップショット / JS bundle)を復元する必要がある場合
  • 使用する場合:JS↔ネイティブブリッジ(MethodChannel / NativeModules)でやり取りされる機密データの場所を特定する必要がある場合
  • 使用しない場合:純粋な Java/Kotlin ネイティブアプリ([[re-apk]] を使用)
  • 使用しない場合:純粋なネイティブ so のロジック([[re-binary-core]] を使用)
  • 使用しない場合:ランタイムフックが必要な場合——まず静的に特定してから [[re-frida]] を使用

ツールの準備

静的解析はサンドボックスなしで実施できます([[re-analyze/platform-tips]] の最優先原則)。動的解析(reFlutter による再パッケージ化 / 実行)は、管理されたデバイス / エミュレーターのスナップショット内で行います。すべてのツールは、使用前に検証してください。

エンジンの識別 —— jadx / unzip([[re-apk]] を再利用)
  • unzip -l app.apk で assets/ と lib/ の構造を確認すれば、エンジンを判別できます。OS ごとのインストール方法は [[re-apk]] のツールの準備を参照してください
blutter —— Flutter AOT スナップショットの静的復元(libapp.so)
  • インストール:git clone https://github.com/worawit/blutter && cd blutter——C++20 プロジェクトです。比較的新しいコンパイラー(g++>=13 / Clang>=16)と cmake/ninja/pkg-config/libicu/libcapstone + pyelftools/requests が必要です
    • Debian/Ubuntu:apt install python3-pyelftools python3-requests git cmake ninja-build build-essential pkg-config libicu-dev libcapstone-dev
    • Fedora:dnf install gcc-c++ clang cmake ninja-build pkgconf-pkg-config libicu-devel capstone-devel python3-pyelftools python3-requests git
    • Arch:pacman -S gcc cmake ninja pkgconf icu capstone python-pyelftools python-requests git
    • macOS:brew install cmake ninja pkg-config icu4c capstone + pip3 install pyelftools requests
  • 使い方:python3 blutter.py libapp.so <输出目录>(Dart のバージョンを自動検出し、必要に応じて Dart ソースコードを自動ダウンロードしてエンジンをコンパイルします)——現在は Android arm64 の libapp.so と比較的新しい Dart バージョンに対応しています
  • 成果物:asm/(シンボル付き逆アセンブル)、objs.txt(オブジェクトプール内のオブジェクトの完全なダンプ)、pp.txt(オブジェクトプール内のすべての Dart オブジェクト)、blutter_frida.js(Frida スクリプトのテンプレート)
  • 検証:出力ディレクトリに pp.txt が生成され、対象アプリのクラス名が含まれていること
reFlutter —— Flutter の動的解析(再パッケージ化 + トラフィックの傍受)
  • インストール:pip3 install reflutter(Python 3、frida-tools に依存)
  • 使い方:reflutter main.apk → Burp プロキシの IP を入力 → release.RE.apk を生成 → uber-apk-signer で再署名(java -jar uber-apk-signer.jar --allowResign -a release.RE.apk)→ インストールして実行
  • 成果物:実行後、adb -d shell "cat /data/data/<包名>/dump.dart" > dump.dart を実行します(クラス / 関数の一覧 + コードのオフセット)
  • 検証:reflutter -h でバージョンと使い方が表示されること。再パッケージ化後にインストールして実行でき、dump.dart が生成されること
  • 注意:Flutter >= 3.24 では、デバイス上でプロキシを手動設定する必要があります(adb shell settings put global http_proxy <ip:port>)
dart ツールチェーン(スナップショット構造の理解用、任意)
  • Arch:pacman -S dart(extra 公式リポジトリ)
  • Debian/Ubuntu / Fedora:公式リポジトリに dart パッケージがありません → Flutter SDK に付属するものを使用します(snap install flutter または公式サイトの SDK)
  • macOS:brew install dart(または brew install --cask flutter);Windows:Flutter SDK zip
  • 検証:dart --version
RN bundle の復元 —— hermes-dec(Hermes バイトコード)
  • インストール:pip3 install hermes-dec(Python 3、標準ライブラリのみ);Ubuntu では snap install hermes-dec も利用できます(snap 版のコマンドには hermes-dec. の接頭辞が付きます。例:hermes-dec.hbc-disassembler。pip 版には接頭辞がありません);Arch AUR では yay -S hermes-dec
  • コマンド:hbc-file-parser(HBC ヘッダー / バージョン / 関数テーブルを解析)、hbc-disassembler(バイトコード → .hasm アセンブリ)、hbc-decompiler(→ 疑似 JS)
  • 検証:hbc-decompiler --help が使用できること。サンプルファイルで 3 つの手順を実行できること
  • プレーンテキストの JS bundle の整形:pip install jsbeautifier(Python 3、js-beautify bundle.js)

操作手順

順番に実行し、各ステップの成果物(so / bundle / hasm / 逆コンパイル出力 + sha256)を保存します。

  1. エンジンの識別:

    unzip -l app.apk | grep -E 'libapp|libflutter|assets/|bundle'
    file lib/arm64-v8a/libapp.so
    file assets/index.android.bundle    # Hermes: "Hermes JavaScript bytecode, version XX"
    
    • Flutter:lib/<abi>/libapp.so(AOT スナップショット)+ libflutter.so(エンジン)+ assets/flutter_assets/
    • RN:assets/index.android.bundle(プレーンテキストの JS または Hermes HBC);RN 0.70 以降はデフォルトで Hermes
    • その他:WebView シェル(assets/*.html)/ Cordova / uni-app——WebView の手順で処理
    • 識別結果に応じて分岐します:Flutter → ステップ 2;RN → ステップ 3
  2. Flutter:libapp.so スナップショットの分析(blutter):

    python3 blutter.py lib/arm64-v8a/libapp.so ./flutter_out
    grep -r "目标类名" ./flutter_out/pp.txt             # 类/函数名在快照字符串表里
    grep -rn "https://\|api\|token\|secret" ./flutter_out/asm/ | head
    
    • blutter の成果物から Dart クラス/関数とオブジェクトプールの構造がわかります——クラス名を手がかりにビジネスロジックを追跡します(release AOT では Dart 層の名前が保持されます。落とし穴 1 を参照)
    • 動的な情報の補足:reFlutter で再パッケージ化 → dump.dart で実行時のクラス/関数とオフセットを取得 → frida -U -f <包名> -l frida.js を _kDartIsolateSnapshotInstructions(readelf -Ws libapp.so)と組み合わせて hook ポイントを特定します
  3. RN:bundle.js の抽出と復元:

    # 明文 JS
    unzip -p app.apk assets/index.android.bundle > bundle.js
    js-beautify bundle.js > bundle.pretty.js
    # Hermes 字节码
    hbc-file-parser index.android.bundle > meta.txt
    hbc-disassembler index.android.bundle out.hasm
    hbc-decompiler index.android.bundle out.js
    # 线索提取
    grep -aoE 'https?://[^"'"'"' ]+' out.js | sort -u     # 端点半程
    grep -aoE '"[A-Za-z0-9_/+=]{16,}"' out.js | sort -u   # 疑似密钥 / base64
    
    • hasm レベルで関数の呼び出し関係を確認します(LoadConstString と CallN の組み合わせ);擬似 JS のループ/条件分岐が不完全な場合は、hasm + 文字列テーブルを基準にします(落とし穴 2 を参照)
  4. ネイティブ部分は [[re-binary-core]] に従って処理:

    • libflutter.so / libapp.so(ELF)→ [[re-format-elf]] で解析 → [[re-ghidra]] でエンジン層の C++(dart:: VM、チャネル登録、Skia)と JNI ブリッジ(FlutterEngine / PlatformChannel)を逆コンパイル
    • RN:libhermes.so(エンジン、任意)、JNI ブリッジ(com.facebook.react パッケージ)→ ネイティブモジュールのロジック
    • デバッグ対策 / 暗号化 / 証明書検証は、この層にあることがよくあります(例:BoringSSL の verify 関数は libflutter.so 内)——hook ポイントはステップ 2 を参照
  5. ロジックの特定(ブリッジ境界):

    • Flutter:MethodChannel のチャネル名を全体検索します(flutter_ プレフィックスがよく使われます);PlatformChannel の送受信箇所が機密データ(token / 設定)の交換ポイントです
    • RN:NativeModules の登録表と requireNativeComponent / NativeModules.xxx——JS 側で見つからない暗号化/復号処理はネイティブモジュール内にあります(ステップ 4 へ)
    • UI テキスト / ネットワーク文字列から逆引きします:文字列テーブル(Flutter スナップショット / JS bundle)→ 対応する関数 → ロジック

Flutter MethodChannel の動的なインターセプト

Flutter のプラットフォームチャネル(PlatformChannel)は Dart ↔ native 通信の中核です。インターセプトすることで、すべてのネイティブ機能の呼び出しを観察できます。

  • Java/Kotlin 側:io.flutter.plugin.common.MethodChannel の MethodCallHandler を hook します——チャネル名 / メソッド名 / 引数 JSON を記録します
  • チャネル名の取得:MethodChannel.setMethodCallHandler を hook します(onEnter でチャネルインスタンスを捕捉し、名前を取得)。または MethodChannel コンストラクターから直接チャネル名を取得します。より低レベルでは BinaryMessenger.send を hook できます(第 1 引数がチャネル名です)
  • エンジンの messenger 層:io.flutter.embedding.engine.FlutterEngine の messenger メッセージ(低レベルのフォールバック)
  • Dart 側の静的な観察を補完:静的解析でチャネル名と呼び出し箇所(文字列リテラル)を特定し、動的解析で実際のトラフィックを確認します
  • 出力:構造化 JSON(channel / method / args)。[[re-analyze/analysis-contract]] のデータ契約で利用します(証拠を保存)

Flutter の詳細については [[re-flutter]] を参照してください

React Native / Hermes(RN バイトコードとブリッジ)

識別:HBC と RN の成果物
  • .hbc / Hermes バイトコード:ファイルヘッダーの先頭 4 バイトは c61fbc03 などのマジック値です(リトルエンディアン)。file の出力は "Hermes JavaScript bytecode, version XX" のような形式です。マジック値がなく、var __BUNDLE_START_TIME__ などの JS ソースで始まる場合は、プレーンテキストの bundle です
  • RN アプリの構造:assets/index.android.bundle(Metro でパッケージ化されたビジネス JS / HBC。RN 0.70 以降はデフォルトで Hermes によるコンパイル)、lib/<abi>/libhermes.so(Hermes エンジン)、libreactnativejni.so(JNI ブリッジ)
  • Hermes と標準的な JS エンジンの違い:Hermes は AOT で事前コンパイルされたバイトコードです(VM 命令を使用し、実行時に JIT コンパイルを行いません)。標準的なエンジン(JSC / V8 系)の成果物はプレーンテキストの JS ソースパッケージです。この違いによって、以降の処理がバイトコードの逆アセンブルか、純粋な JS の復元かに分かれます
Hermes バイトコード:逆アセンブルの考え方
  • 構造:ファイルヘッダー(マジック値 + バージョン番号)→ バイトコードセクション → 関数テーブル / 文字列テーブル;バージョン番号はファイルヘッダーにあり、使用できるツールを直接左右します
  • 逆アセンブル:hermesc 系のツール(-dump-bytecode 系)または HBC 逆アセンブラー系の Python 実装(「ツールの準備」を参照)→ hasm アセンブリを出力します;文字列テーブルはまとめて抽出できます(セクションをまたいで印字可能な文字列を取得)ので、まず手がかりを得ます
  • 関数テーブルの特徴:各関数には id / オフセット / 命令数 / 引数の数が含まれます——呼び出し関係(LoadConstString と CallN の組み合わせ)をたどってビジネスロジックの入口を追跡します
JS bundle の抽出:Metro の構造と復元
  • Metro のパッケージ構造:モジュールファクトリーは __d(factory, moduleId, deps) によってグローバル登録表に登録され、__r(moduleId) が実行時に require します;bundle の先頭には __BUNDLE_START_TIME__ と polyfill のプレフィックスがあります
  • ビジネス JS の復元:整形(jsbeautify 系のツール)→ __d( でモジュール境界を分割 → 各モジュールを個別に復元(ファクトリー本体 + 依存関係配列)→ 難読化 / 文字列暗号化の処理は [[re-script-deob]] に続きます
  • モジュールの対応付け:サイズの大きなモジュール(文字列が長い / 依存関係が多い)はビジネスロジックのメインパッケージであることが多いため、画面機能と照合して対象を絞り込めます
ネイティブブリッジ:NativeModules / TurboModule
  • JS 側の特定:NativeModules.xxx のアクセス箇所 / TurboModuleRegistry.get('Xxx')——ブリッジ名がそのままモジュール名です
  • ネイティブ側の対応付け:JNI ブリッジの com.facebook.react パッケージ配下にあるモジュール登録表(@ReactMethod アノテーション付きのメソッド / getNativeModules() の一覧);TurboModule のインターフェースメソッドのシグネチャは JS の呼び出し名と 1 対 1 で対応します
  • ブリッジ境界が機密データの交換ポイントです:JS 側で見つからない暗号化/復号処理はネイティブモジュール内にあります([[re-binary-core]] へ)。Flutter MethodChannel と同様です
動的解析:JS ランタイムの hook([[re-frida]])
  • モジュールのロード箇所:__d / __r を hook すると、ロード済みの全モジュールと id を列挙し、モジュールファクトリーのソースを dump できます。実行時に追加登録されたモジュールは、リモート配信(ホットアップデート)の特徴です
  • console 出力:console.log / warn の実装を hook し、ビジネスログとエラースタックを取得します
  • ブリッジ層のフォールバック:NativeModules のメソッド呼び出しを hook し(引数 / 戻り値)、JS↔ネイティブ間で交換されるデータを観察します
坑与陷阱
  • Hermes 字节码不跨版本:现象——同一样本在一种环境下反汇编正常,换版本或换工具输出乱码、直接报错;原因——HBC 格式随引擎版本演进、不保证向后兼容,反汇编类工具只覆盖特定版本区间;对策——先取版本(file 输出 / libhermes.so 内版本字符串 / 构建时间),选对应版本的 hermesc 类工具或更新反汇编工具,版本不匹配时优先换工具而非手动修字节
  • bundle 加密 / 混淆(Metro 加密层):现象——bundle 解出后是密文 / 乱码,或模块函数名不可读;原因——构建管线在 Metro 输出层加密(bundle 内嵌解密器,运行期解密)或启用混淆插件;对策——在 bundle 内找解密器特征(XOR 循环 / 自解密函数 / 硬编码密钥),静态还原解密器整体解密,或 [[re-frida]] 在解密函数返回处 dump 明文,混淆部分衔接 [[re-script-deob]]
  • 字节码与明文 JS 混合(部分页面走远程 bundle):现象——本地 bundle 是明文 JS 且内容正常,但关键页面逻辑缺失,或运行期出现本地没有的模块;原因——混合交付:部分页面打包进 assets,敏感 / 更新页走远程下发 bundle(热更新、灰度),本地包只含壳与加载逻辑;对策——先核对本地 bundle 完整性(__d 模块数与页面功能对比),抓包 / 文件系统快照归档远程 bundle,再按明文或 HBC 路径处理

跨域联合

  • [[re-mobile]]: 混合应用分支固定调用本技能(引擎识别后按框架分流)
  • [[re-apk]] / [[re-ios]]: 容器侧静态(manifest、签名、iOS 等价物)
  • [[re-binary-core]]: 原生部分——[[re-format-elf]] + [[re-ghidra]] 反编译 libapp.so / libflutter.so 与 RN JNI 桥
  • [[re-frida]]: 运行时 hook(桥接层、证书校验、反检测)
  • [[re-analyze/platform-tips]]: 默认沙箱、工具解析 ≠ 加载器视图(快照偏移 / 工具版本差异)
  • 本技能被 [[re-analyze]] 的 triage「移动 App 分析」路径引用(re-mobile → re-hybrid-app)

常见坑与陷阱

  • Flutter AOT スナップショットにはソースレベルのシンボルがない:現象——IDA/Ghidra で libapp.so を直接見ると dart:: 内部関数と生のオフセットしか表示されず、ビジネス関数名が見えません。原因——AOT スナップショットには DWARF シンボルがありませんが、Dart のクラス名/関数名は実際にはスナップショットの文字列テーブルに残っています。対策——blutter で復元(pp.txt / asm/)し、文字列テーブルを grep してクラス名を特定したうえで asm と照合して分析します。ELF シンボルテーブルでビジネスシンボルを探さないでください
  • RN Hermes のバイトコードは復元が困難:現象——hbc-decompiler が出力する疑似 JS ではループ/条件がすべて欠落し、変数名がレジスタ番号になるため、ロジックを読み解けません。原因——HBC は VM 命令であり、ソースレベルへの逆コンパイルは不完全です。対策——hbc-disassembler の hasm で呼び出し関係を確認し(LoadConstString + CallN で文字列の使用箇所を特定)、文字列テーブル/bundle 内の平文(URL、エラーメッセージ)から意味を逆引きします。必要に応じて、動的に取得した平文の通信と照合します
  • エンジンのバージョン差異:現象——blutter が Dart バージョン非対応と報告する/hermes-dec の解析が失敗する、またはずれます。原因——ツールが対応するのは特定のバージョン範囲のみで、アプリが新しいエンジン(Dart 3.x の新しいスナップショット形式、Hermes の新しい bytecode バージョン)を使用しています。対策——まずバージョンを取得し(libflutter.so 内のバージョン文字列/file の出力する HBC version/ビルド日時)、ツールを更新する(blutter は必要に応じて対応する Dart ソースをダウンロードして再コンパイルします)か、そのバージョンに対応した専用ツールに切り替えます
  • JS とネイティブのブリッジ境界:現象——JS/bundle 内に暗号化・復号や鍵のロジックが見つかりません。原因——機密性の高いロジックがネイティブモジュール(RN NativeModules / Flutter MethodChannel のネイティブ側)にあります。対策——手順 5 に沿ってブリッジの登録(チャネル名/ネイティブモジュールの一覧)を探し、[[re-binary-core]] に進んで対応する so を分析します。JS 層にこだわり続けないでください
  • 難読化されたビルド:現象——関数名がすべて a/b/c または hash です。原因——Flutter --obfuscate/RN Hermes + 難読化オプションでビルドされています。対策——利用可能な文字列(エラーメッセージ/API パス)と動作観察を軸に分析し、動的な [[re-frida]] hook でブリッジ層の特定を組み合わせます
  • Flutter の SSL 検証によりパケットを取得できない:現象——パケットキャプチャツールに Flutter アプリの通信が表示されない、または証明書エラーが出ます。原因——Flutter は独自の SSL 検証を備えており(システムプロキシ/証明書の信頼チェーンを経由しません)。あらゆるアプリに対応するソリューションのオープンソースコードが github にあります。対策——まず Flutter の SSL 検証に対応するソリューションを使うか、frida で回避してパケットを取得します。blutter で復元すると libapp.so のシンボルが見えるため、アドレスを基に対象関数を直接 hook できます(たとえば署名関数 generateMD5 の引数/戻り値からソルト値と平文を取得します)。アルゴリズム全体を復元する必要はありません
  • Dart 文字列のメモリレイアウト(frida で文字列を読み取る際の要点):現象——libapp.so の関数を hook してポインターを取得しても、文字列を読み取れません。原因——Dart 文字列は C 文字列ではありません。ポインター +7 の位置に 4 バイトの Smi エンコードされた長さ(右に 1 ビットシフトすると実際の長さ)、+15 の位置に UTF-8 データがあります。対策——このレイアウトに従って readDartStringExact ユーティリティ関数を作成します(誤読防止のため長さの上限を設定します)。hook した引数/戻り値のどちらからも平文を復元できます
  • Hermes の関数テーブルヘッダーは構造体ではなく、ビットフィールドで圧縮されています:現象——関数テーブルを通常の構造体として読むと、関数の境界/名前/引数の数が乱れたり、オーバーフローした関数の大きなヘッダーのオフセットを通常のフィールドとして読み取ったりします。原因——HBC の関数ヘッダーは 10 個のフィールドを 1 つの 128 ビットワード(legacy は 16 バイト、バージョン <97)または 96 ビットワード(modern は 12 バイト、バージョン >=97、RN 0.75+ のデフォルト形式)に格納しており、ビット幅の割り当てはバージョンによって異なります。対策——まずバージョン番号を取得してからビットフィールドの配置を選択します。flags ビットは意味の推定にも役立ちます(bit0 strict、bit3 hasExceptionHandler、bit5 overflowed)。overflowed が設定されている場合、インラインヘッダーフィールドは信頼できないため、大きなヘッダーに基づいて組み立てます(legacy は (info_offset<<16)|offset、modern は (function_name<<24)|(offset&0xffffff))。関数の境界 = ヘッダー内のオフセット + bytecode_size、グローバルコードは関数 0 です(出典:hermes-decomp(SymbioticSec)、MIT)
  • Hermes の小文字列テーブル:u32 ビットフィールド + センチネルによるオーバーフローテーブル + run-length エンコード + packed 共有ストレージ:現象——文字列の長さ/内容がずれたり、xref の逆引き先が誤った関数になったりします。原因——各項目は 1 個の u32 で、bit0 は UTF-16 フラグ、bits1-23 はストレージオフセット、bits24-31 は長さ(最大 255)を格納します。長さまたはオフセットがセンチネル値(0xff / 0x800000)に達した場合、実際の値はオーバーフローテーブルに (offset,length) のペアで補足されています。文字列種別テーブルは run-length エンコード(最上位ビット 0=通常の String、1=Identifier、下位 31 ビット=同じ種別が連続する個数)です。対策——ビットフィールドに従って解析し、オーバーフローテーブルを参照します。項目は必ずテーブル項目のオフセットで特定し、部分文字列検索は絶対に行わないでください(複数の項目が同じストレージ領域を共有している場合があります)。書き戻し時も同様です。run を展開すると、コード文字列と識別子を区別できます(識別子はプロパティアクセスの解析と名前の推定に使われます)(出典:hermes-decomp(SymbioticSec)、MIT)
  • opcode の定義はバージョンごとに変わりますが、builtin インデックスが変わるのはリリース境界のみです:現象——同じバイトコードでも逆アセンブル/逆コンパイルツールやバージョンを変えると出力がずれたり、未知の opcode に遭遇してクラッシュしたりします。原因——HBC のバージョンごと(40-99)に命令テーブルがあり、opcode 番号とオペランド型の並び(Reg8/Reg32/UInt8/Addr8 など)はバージョンごとに異なります。対策——バージョンごとにテーブルを取得します(バージョンが欠けている場合は「対象バージョン以下で最も近い」テーブルにフォールバックし、実際のバージョンを報告します)。未知の opcode は範囲外アクセスではなく にフォールバックします。逆アセンブル層ではまず意味上のバリエーションを抽象定義に正規化します(2 種類の Mov エンコード、3 種類の GetById バリエーションを同じ抽象操作に統合します)。IR 層では抽象的な意味だけを処理します。ジャンプ先は Addr8/Addr32 に従って自身のオフセットからの相対位置として計算します(出典:hermes-decomp(SymbioticSec)、MIT)
  • 変数の復元に完全な SSA+Phi は使わないでください:現象——逆コンパイル出力に一時変数とエッジコピーが大量に含まれ、読みやすさが低下します。原因——対象言語の JS に Phi はなく、完全な SSA の De-SSA 段階はかえってノイズを生みます。対策——生存区間分割方式で復元します。到達定義 + Union-Find を使い、2 つの定義が同じ使用箇所(if/else の合流点、ループのバックエッジ)に集まる場合は 1 つのクラスに統合し、それ以外は別々の変数にします。Phi は使いません。名前付けではまずレジスターの役割を推定します(プロパティ/メソッドの語彙から型を推測します:length/push/map/filter→Array、then/catch/finally→Promise、next/done/value→Iterator、toString/split/trim→String)。名前の優先順位は、デバッグセクションのスコープ名 > 分割代入のキー名 > 名前付き関数名 > 引数 > 型によるフォールバック > 番号です。関数名は「有意性」フィルターに通し、純粋な数字/生成名/汎用的なコア名は除外します(出典:hermes-decomp(SymbioticSec)、MIT)
  • Metro のモジュールファクトリーの引数の数はバージョンの手掛かりです:現象——__d 登録の走査でファクトリーを認識できず、ファクトリー引数の役割を取り違え、モジュールの骨格を復元できません。原因——classic bundle のファクトリーは 4 引数(require/module/exports/deps)、modern bundle は 7 引数(importDefault/importAll を含む)です。対策——param_count-1(this を除いた数)を役割判定の根拠にします。__d(undefined, factory, moduleId, deps) のパターンに一致させます。引数がレジスター経由で転送される場合は、「レジスター→関数/整数/配列」の関数内 map を維持して値を追跡します。エクスポート分析では、直接代入/一括代入/Object.defineProperty の 3 パターンに分けます。モジュール名は、単一箇所の推定よりも伝播チェーン(require の名前を逆引き→再エクスポートする薄いラッパーが継承→単一依存チェーン)に沿って推定するほうが信頼性が高くなります(出典:hermes-decomp(SymbioticSec)、MIT)
  • バイトコードの書き戻しパッチ:同じ長さならインプレース、長さが変わるなら移動、重複がある場合は拒否:現象——.hbc のバイトを直接変更すると bundle の実行がクラッシュしたり、構造テーブルがずれたりします。原因——文字列ストレージは packed に対応しており(複数の項目が同じ領域を共有)、オフセットは複数箇所から参照されています。長さが変わる変更では、後続のすべての領域を連動して修正する必要があります。対策——JS を再コンパイルせず、バイトコードだけを変更します(HASM の往復:emit-hasm で出力し、編集してから asm に戻します)。文字列パッチは長さに応じて振り分けます。同じ長さならテーブルを変更せずインプレースで上書きし、長さが変わる場合は文字列テーブルとストレージを再構築して末尾のすべての領域を移動します(オフセットも連動して修正します)。UTF-16 文字列と packed で重複している文字列へのパッチは必ず拒否します。modern(>=97)では同じ長さの文字列パッチのみをサポートし、長さが変わる文字列/関数の resize/stub の挿入による拡張/ゼロからの create はすべて明示的にエラーを報告して、破損した出力を書き込みません。パッチ後に再解析して構造の整合性を検証し、整合しなければ出力を拒否します(出典:hermes-decomp(SymbioticSec)、MIT)
  • Hermes の逆コンパイルの能力範囲:現象——逆コンパイル結果に変数名がなかったり、制御フローが不正確だったり、元のソースコードをそのまま取得できると期待したりします。原因——逆コンパイラーには難読化解除の機能がなく、難読化後のコードから得られるのは難読化後の意味だけです。リリースパッケージではデバッグセクションが削除されていることが多く(offset=0 または u32::MAX)、変数名/行番号が完全に失われています。逆アセンブル≠逆コンパイルです。命令ストリームから正確な制御フローとオペランドは得られますが、変数/構造/モジュールの意味は IR + プログラム全体の分析に依存します。対策——「デバッグ情報なし」自体も分析結果です。名前はヒューリスティックで推定するしかありません。まず文字列テーブルでコンパイル時に埋め込まれたソース/JSON/マッピングを探します(例:RN Reanimated の Babel プラグインは worklet の元ソースを通常の文字列として .hbc の文字列テーブルにコンパイルするため、関数名で join すれば実際のソースコードを取得できます)。逆コンパイルよりも手間が少なく、忠実度も高くなります(出典:hermes-decomp(SymbioticSec)、MIT)

コメント