はじめに
こんにちは、セキュリティ・エンジニアの桑原です。 このたび、社内でサイバー分析サークルという技術サークルを立ち上げました。 このサークルでは、サイバーセキュリティに関する知見を深め、それを社内に展開していくことを目指しています。 せっかくなので、サークルで得た学びや気づきを、ブログでも少しずつ紹介していきます。
今回は 「AI を活用したマルウェア解析の効率化」 について、Ghidra の基本的な操作を把握している人向けに解説します。 実際に試してみた内容を交えながら基本的なものを取り扱いますので、AI を用いた解析のイメージを掴む助け になるはずです。
マルウェア解析では、静的解析などを通じて挙動を読み解く作業に多大な労力を要します。 しかし、AI を活用したツールを用いることで、このプロセスを大幅に効率化できます。 従来の静的解析では、人間の解析者がアセンブリやそれをデコンパイルした C 言語のコードを読み、関数の目的を推測し、変数や関数に適切な名前を付けていく必要がありました。 AI はコードの文脈を理解し、命名や挙動解釈を提示できるため、解析者はより深い考察に専念できます。
利用するツール・マルウェア
Ghidra に導入できる AI ベースのプラグイン KinGAidra を使い、AI を活用したマルウェア解析する方法を解説します。 ここでは、MCP (Model Context Protocol) のような高度な連携はあえて扱わず、手軽に利用できる方法に絞ります。
- Ghidra: 11.3.1
- KinGAidra: 1.1.0
解析対象のマルウェアは、MWS Cup 2023 の静的解析課題にて題材にされた以下の検体です。
780f5d21f1f38779f643f1fdf6c42795d23f7e77e1f75b09cead2ce5d0f15ea3
また、AI として ChatGPT の 4o-mini を API 経由で利用します。 AI を多用し様々なマルウェアを解析していくのであれば、価格と性能のバランスを考慮すると 4o-mini が適していると考えています。 上位モデルは必要に応じてスポット的に利用すると良いでしょう。
KinGAidra 概要
KinGAidra は、AI を活用してリバースエンジニアリングを支援する Ghidra 用プラグインです。 詳細は README を参照してください。
以下では、後の説明で利用する UI のみ説明します。
KinGAidra ウィンドウ
- Ghidra のメニューの「Window -> KinGAidra」から開きます。
- 3 つのタブがあります:
Chat: アセンブリやデコンパイル結果について AI と対話Decom: デコンパイル結果の関数名や変数名を提案・適用KeyFunc: 解析対象として優先度の高い関数を列挙
コンテキストメニュー
- Listing/Decompile ウィンドウで右クリックすると表示されます。
- 主なコマンド:
Explain using AI: 関数の挙動を AI が解説Decompile using AI: アセンブリを AI がデコンパイルRefactoring using AI: デコンパイル結果を読みやすくリファクタリングAdd comments using AI: デコンパイル結果理解のガイドとして働くコメントを挿入
KinGAidra の準備方法は記事末尾にまとめています。
AIを活用したマルウェア解析
以下の流れで AI を活用し、どのように解析を加速できるかを紹介します。
解析の起点探し
はじめに、バイナリ全体を俯瞰し、解析の起点となる重要な関数を特定します。 AI は対象バイナリ全体を調査し、たとえば暗号化を行う関数や情報窃取に関わる関数など、解析対象として優先度の高い関数をリストアップしてくれます。 この一覧を手がかりに、膨大な関数群から調査対象を迅速に絞り込むことができます。
KinGAidra では KeyFunc タブでバイナリに含まれる文字列をベースに調査し、関数を列挙します。
KeyFunc タブで Guess ボタンを実行した結果が以下です。
解析対象として優先度の高い関数が、その根拠となる文字列とともに列挙されています。
図の一覧を見ると、ログのような文字列がいくつかあります。
Can't find Func %hs ! や Load %s error!\r\n はどちらも関数 FUN_01221f70 に関連し、動的な API 取得が行われていそうです。
UploadFile や DownloadFile、shell などが含まれた文字列に関連する関数は RAT のコマンドが実装された関数であると推測できます。
ログ以外の文字列を見ると、ADD24D415FEC26FF2D321F50AB7574F873 のようなランダムな文字列も含まれており、暗号化系の処理があると考えられます。
CONNECT %s:%d HTTP/1.1... などの文字列からは、通信処理を含んでいると予想されます。
このように、解析する関数を決めるにあたりヒントとなる情報が得られます。
次のステップでは、文字列 Can't find Func %hs ! と関連する関数 FUN_01221f70 を見ていきます。
関数の挙動把握
アセンブリやそのデコンパイル結果を見て関数の挙動を明らかにするのは非常に骨が折れる作業です。
ここでも AI を活用して、関数 FUN_01221f70 の挙動を把握していきましょう。
Ghidra のコンテキストメニューから Explain using AI を実行すると、図の左側の Chat タブに当該関数の目的や内部処理がステップごとに表示されます。
図の右側は Ghidra によるデコンパイル結果です。
左側の関数解説の末尾の「期待する挙動」では第 2 引数の param_2 で指定された関数名の関数ポインタを Kernel32.dll から取得することが目的であると解説されています。
「ロジックの分解」でこれらの挙動の詳細が解説されています。
「3. DLL の取得」では GetModuleHandleA と LoadLibraryA による Kernel32.dll の取得について解説されています。
「4. 関数アドレスの取得」では GetProcAddress による param_2 で指定された関数のアドレス取得について解説されています。
右側のデコンパイル結果を見るに、これらの解説は正しそうです。
「5. 条件分岐とエラーハンドリング」では関数ポインタの取得失敗時の挙動が解説されています。
デコンパイル結果の当該箇所にあたる 25 行目から 31 行目では関数名やその引数の情報はほとんど得られません。
しかし、FUN_01221f30 は情報をロードする関数などと解説されています。
手動で 0x124ecec を確認すると、Can't find Func %hs ! を指しており、この関数は sprintf などの文字列を生成する関数と推測できます。
情報をロードする関数という解説は合っているようですが、AI にはデコンパイル結果のみしか渡されていないため、解説は限定的なものになっています。
Decompile using AI を実行すると Chat ウィンドウにアセンブリを AI によってデコンパイルした結果が表示されます。
AI のデコンパイル結果では第 2 引数がうまく解決されていませんが、Kernel32.dll から API を取得する流れが解説されています。
加えて、Explain using AI では不明だった、ログ周りの処理や関数名が解決されています。
FUN_01221f30 と FUN_01221e80 はそれぞれ sprintf と errorMessage になっています。
sprintf で生成された文字列が変数 buffer に格納され、errorMessage の引数として渡されます。
以下は AI へのデコンパイル依頼の一部抜粋です。
アセンブリを見ると FUN_01221f30 の呼び出し直前に、0x124ecec、EAX の順でスタックに積まれています。
このことから、0x124ecec は FUN_01221f30 の第 2 引数であると分かります。
今回は、文字列情報が解決されていたため AI がより詳細な解説を出力できたようです。
これらをチャットの会話で質問し続けることも可能です。
また、KinGAidra ウィンドウ右上の History から過去のチャットを選択することで、過去のチャットの会話を続けることも可能です。
デコンパイル結果の修正
次に、この関数のデコンパイル結果を修正していきます。 関数名や変数名をわかりやすいものに変更し、コメントを追加することで可読性が向上し、後からコードを読み返す際やチームで共有する際の負担を大幅に減らせます。 さらに、関数に適切な名前を付けておくと、呼び出し元を解析するときの理解も容易になります。
コンテキストメニューから以下を実行すると選択中の関数のデコンパイル結果に手を加えることができます。
Refactoring using AIAdd comments using AI
関数 FUN_01221f70 にこれらの機能を適用します。
Refactoring using AI を実行するとリファクタリング候補が Decom タブに表示されます。
Guess コマンドを押すと新たな候補がタブとして追加されます。
Old 列に元の名前、New 列にリファクタリング後の名前が載っています。
関数名は LoadKernel32ModuleAndGetProcAddress、第 2 引数は functionName と提案されています。
ローカル変数も GetProcAddress の戻り値を格納する変数 functionAddress のようにそれぞれ変更されます。
適用したい候補を選択して Refactoring ボタンを押すとリファクタリングが適用されます。
以下がリファクタリング結果です。
上記変更が適用され読みやすくなりました。
さらに、Add comments using AI を実行するとデコンパイル結果にコメントが追加されます。
以下がコメントの追加結果です。
各種 API の呼び出し箇所を見ると、その処理の意味がコメントとして追記されています。
50 行目から 62 行目では GetModuleHandleA や LoadLibraryA、GetProcAddress がそれぞれ何を行っているかが解説されています。
加えて、52、56, 59, 69 行目では、各 API の成功・失敗時の挙動について解説されており、コードフローが辿りやすくなっています。
このように、関数・変数名の修正とコメントの追加により、関数がかなり読みやすくなりました。
KinGAidraの準備
KinGAidra の導入手順は README の Installation と Configuration を参照してください。 ここでは今回適用した設定は以下です。 この設定により、ChatGPT の 4o-mini の利用と日本語対応が可能になります。
URL:https://api.openai.com/v1/chat/completionsMODEL:gpt-4o-miniAPI_KEY:<APIキー>POST_MSG:"Please respond in Japanese"
おわりに
KinGAidra を用いた基本的なマルウェア解析方法を紹介しました。 AI を活用することで、解析作業の負担が軽減され、効率的に進められることを実感できたのではないでしょうか。 ただし、AI の出力を鵜呑みにするのは禁物です。 必ずオリジナルのアセンブリやデコンパイル結果と突き合わせて検証してください。 解析結果を何らかの形でアウトプットする際、その正しさに責任を持つのはあくまで解析者自身です。 AI で解析のほとんどを自動化できたとしても、あくまで最終確認は人間の手で行う姿勢が重要です。
今回は AI を利用したマルウェア解析の基本的な部分を説明しやすくするため、私が開発している KinGAidra (個人活動のため会社とは関係ありません) を利用しました。 本当は GhidrAssist も取り上げたかったのですが、ボリュームの都合で割愛しました。 GhidrAssist では、RAG や RLHF (0.27.0 時点ではフィードバックを受け付けるだけで学習は未対応) といった技術にも対応しており、注目しています。 詳細は README を参照してください。
セキュリティの分野では秘密保持のため、外部サービスに情報を挙げられないケースも少なくありません。 次回はローカル LLM を用いた解析について取り上げる予定です。 どうぞお楽しみに。
エンジニア募集
FFRIセキュリティではサイバーセキュリティに関する興味関心を持つエンジニアを募集しています。 採用に関しては 採用ページ をご覧ください。







