diffing-binary-patches について
リバースエンジニアや脆弱性分析者向けのバイナリパッチ差分分析スキルです。修正前後のビルドを比較し、ロジックの変更箇所と、信頼できない入力からの到達性を調べます。
主な機能
- 01バイナリのバージョン比較
- 02Microsoft パッチバイナリの再構築
- 03変更された関数の特定
- 04差分に含まれるセキュリティ検査の解釈
- 05入力到達性の追跡
- 06公開アドバイザリーの Markdown 取得
活用シーン
- 01ベンダーのセキュリティ更新の分析
- 02コンパイル済みファイルを使った CVE 調査
- 03告知されていない修正の評価
- 04変更コードに至る到達経路の特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill diffing-binary-patchesインストール後、Claude Code で/diffing-binary-patchesを入力して読み込まれたか確認します。
詳細
バイナリパッチの差分解析
セキュリティパッチとは、ベンダー自身のコードで記述された脆弱性への対処です。修正前後のバイナリを差分解析すると、1行のセキュリティ情報から、該当する正確な関数、追加された正確なチェック、そして逆向きに推論することで、パッチ適用前にバグへ到達した入力を特定できます。これが1-day解析の核心です。修正内容が調査箇所を示し、差分が変更内容を示し、パッチ適用前の到達可能性がその方法を示します。
使用する場面
- 同じバイナリ(DLL、ELF、ドライバー)の2つのバージョンを比較して、セキュリティ更新で何が変更されたかを調べる
- CVEのセキュリティ情報やPatch Tuesdayの告知から、具体的な脆弱性のあるコードを特定する
- Microsoftのパッチ(MSU/MSP/cab、差分圧縮)を展開し、差分解析できる完全なバイナリを取り出す
- 新しいリリースでの「サイレント」な修正がセキュリティ上のバグを解消したかどうかを評価する
使用しない場面
- パッチ適用済みの対応物がない状態で単一のバイナリを理解する場合 — その場合は
analyzing-binariesを使用してください。 - バグとトリガーを理解した後に実際に動作するエクスプロイトを作成する場合 —
ネイティブコードでは
exploiting-memory-corruption、Webでは該当するスキルを使用してください。 - ソースコードレベルの差分(GitHubのコミットや変更履歴)の場合 — ソースコードは
reviewing-code-changesで確認してください。バイナリ差分解析はコンパイル済みの成果物しかない場合に使用します。 - 差分解析の前に保護されたバイナリを展開する場合 — 先に
unpacking-protected-binariesを使用してください。
比較可能な2つのバイナリを用意する
差分の精度は入力の品質に左右されます。同じバイナリの2つのバージョンが必要です。アーキテクチャを揃え、できればコンパイラーも同じにして、ノイズを抑え、セキュリティ上の変更を際立たせます。
Microsoftのパッチでは、これ自体がひとつの工程です。Windows 10以降、更新プログラムはファイル全体ではなく、前方/逆方向の差分パッチとして配布されます。MSUに含まれているのは圧縮された差分であり、完成したDLLではありません。更新プログラムを展開(MSU/cabをexpand)し、差分をベースラインのバイナリに適用して、パッチ適用済みの完全なファイルを再構築します(PatchExtract / delta_patch.py の方法で msdelta APIを利用します)。未加工の差分や、対応していないベースラインを差分解析すると、意味のない結果になります。それ以外の多くのソフトウェアでは、2つのリリースビルドまたは2つのパッケージバージョンをそのまま比較できます。
変更された関数を見つける
バイナリ差分ツールを実行し、結果を上から順ではなく類似度に基づいて確認します。
- BinDiff(オープンソース。IDAまたはGhidraのBinExportを使用)は、2つのバイナリの関数を対応付け、類似度をスコア化します。注目すべきなのは、完全に新しく追加された関数や削除された関数ではなく、わずかに変更された関数、つまり類似度が1.0をわずかに下回る関数です。
- Diaphora(IDA)は、強力なヒューリスティックと優れた疑似コード差分表示を使って同様の処理を行います。変更内容を理解するためにデコンパイラーの出力を読むので、これは重要です。
- ghidriff は、ヘッドレスまたはCI形式で実行したい場合に、スクリプトから操作できるGhidraベースの差分解析を提供します。
ノイズは発生するものと考えてください。コンパイラーの変更、関数の並べ替え、無関係な機能によって差分が膨らむことがあります。ASLRやアドレスのずれは実際の変更ではありません。ロジックが変わった関数に絞り、アドレスだけの変更は無視してください。
修正を読み解き、バグを特定する
追加されたコードが手がかりです。セキュリティ修正の大半は、いくつかの典型的なパターンに分類でき、それぞれがバグの種類を示します。
- コピーやインデックス参照の前に新しい境界チェックまたは長さチェックが追加された → 修正前のコードでは、その経路でオーバーフローまたはOOBアクセスが発生していました。
- 符号の扱いまたは整数型が変更された(
size_tをintに使用、オーバーフローチェックの追加など) → 割り当てやコピーに影響する整数のバグです。 - 新しいnullチェックまたは状態チェックが追加された → チェックによって防がれるようになったのは、use-after-free、競合状態、または未初期化の値の使用です。
- パースした入力から得られるフィールド、型タグ、または長さを検証する処理が追加された → パーサーが攻撃者の制御するデータを信頼していました。
- 解放処理の順序が変更された、または解放後にポインターをnullに設定する処理が追加された → UAFまたは二重解放です。
まずパッチ適用後の関数を読んで、現在適用されている制約を確認し、次にパッチ適用前の関数を読んで、その制約がなかったことを確認してください。この2つの関数の違いが脆弱性です。
トリガーまで遡る
変更された関数は手がかりであり、実際に到達できるまでは発見事項ではありません。攻撃者が制御する入力が、パッチ適用前のコードにどのように届くかを追跡します。どの API、メッセージ、ファイルフィールド、またはパケットがガードされていない経路に到達するのか、そしてどのような状態で到達するのかを確認してください。信頼できない入力から変更されたコードまでの経路を構築できない場合、その変更は到達可能なバグの修正ではなく、堅牢化である可能性があります。過大な主張はせず、その旨を伝えてください。経路が実在する場合、その到達可能なトリガーがエクスプロイト開発への引き継ぎになります。
外部ソースの読み取り
公開されているアドバイザリ、仕様書、ベンダーレポートは、Markdown として取得します。
curl -sL "https://defuddle.md/<url>" # scheme in the path is optional
これによりページの定型部分が取り除かれます。文章中心のページではトークン数が約78%減り、要約ではなく全文が返されるため、grep で検索でき、該当情報がないという検索結果も信頼できます。
この方法を使うべきでないケースは3つあります。JSON や API レスポンスは、読みやすさを優先した抽出で構造化データが壊れるため、そのまま取得してください。認証が必要なページや JavaScript で描画されるページは、匿名で取得されるため、直接取得してください。そして、敵対者のインフラストラクチャ(フィッシングリンク、C2、マルウェアホスティング)、クライアント所有のホスト、エンゲージメント URLは、決してこの方法で経由させないでください。リクエストがマシンから第三者に送信され、稼働中の敵対者のインフラストラクチャの場合は、運用者に気付かれることにもなります。
サイトによっては抽出ツールをブロックし、ページの代わりにエラーの内容を返します。たとえば、freedesktop.org では {"error":"Failed to fetch: 418 I'm a teapot"} が返されます。これは取得が拒否されたということであり、ソースに該当の情報が存在しないという意味ではありません。それを根拠に結論を出す前に、URL を直接再取得してください。
退けるべき合理化
- 「変更された関数が200個もあるので、このパッチは差分を追えません。」 ほとんどはコンパイラやレイアウトに起因するノイズです。類似度順に並べ、ロジックの変更に絞り込んでください。セキュリティ修正は通常、1個から数個の関数に収まります。
- 「MSUを直接 diff したら、意味不明な結果になりました。」 Windows で配布されるのは完全なファイルではなく差分です。diff を取る前に、その差分とベースラインからパッチ適用後のバイナリ全体を再構築してください。
- 「関数が変更されているので、これが脆弱性です。」 変更は手がかりです。信頼できない入力からパッチ適用前のコードに至る到達可能な経路がなければ、堅牢化を見ている可能性があります。バグを主張する前に、到達可能性を立証してください。
- 「アドバイザリにRCEと書かれているので、これがそのRCEです。」 アドバイザリは意図的に曖昧に書かれています。報告書の宣伝文句ではなく、追加されたチェックからバグの種類を確認してください。
- 「同じバグなので、古いエクスプロイトが使えます。」 修正によって、隣接するコード、オフセット、オブジェクトのレイアウトが変わることがあります。実際に手元にあるパッチ適用前のビルドで、プリミティブを改めて確認してください。
参考資料
analyzing-binaries— どちらか一方のバイナリを単独で理解する場合exploiting-memory-corruption— 復元したバグをもとにエクスプロイトを構築する場合reviewing-code-changes— バイナリではなくソースコードの差分がある場合unpacking-protected-binaries— diff を取る前にバイナリを展開する必要がある場合