ページの先頭です


ページ内移動用のリンクです

  1. ホーム
  2. IIJの技術
  3. セキュリティ・技術レポート
  4. Internet Infrastructure Review(IIR)
  5. Vol.59
  6. 2. フォーカス・リサーチ(1)CTOとCTO Function Listerを使ったマルウェアの解析方法

Internet Infrastructure Review(IIR)Vol.59
2023年6月23日発行
RSS

目次

2. フォーカス・リサーチ(1)

CTOとCTO Function Listerを使ったマルウェアの解析方法

2021年に行われたVirus Bulletin(VB2021 localhost)において、CTO及びCTO Function Listerというツールを発表しました(注1)。その後も断続的ではありますが、機能追加などの改善を行っています。本稿では、これらのツールをマルウェア解析のどの部分に適用しているのかを、実際の解析事例と共に紹介します。
なお、今回使用した検体はselfmake3という、SpiderPigと呼ばれるRATをダウンロードして実行するためのダウンロード型マルウェアで、標的型攻撃で使用されたものです。SHA256ハッシュ値は以下のとおりです。

7DA969010A55919AA66ED97A2D2D6D6A0BE3D8DC6151EEB6CEBC15E4F06D4553

2.1 起動方法と初期画面

CTO、CTO Function Listerは共にIDA Pro(注2)上でプラグインとして動作します。起動はEditメニューのPlugins、ツールバーのボタン、ショートカットキーのいずれかから行うことができます。図-1では、IDAウィンドウのツールバーの右端に中年男性風のアイコンがあるのが見えます。これらがCTOとCTO Function Listerのアイコンです。これらをクリックすることでウィンドウの左側にCTO Function Listerが表示され、右側にCTOが表示されます。CTOは主に関数呼び出しの親子関係を可視化するツールです。CTO Function Listerは関数一覧や、それぞれの関数が持つ特徴を抽出して保持し、フィルタリング機能でそれらの情報を一括検索するのが主な機能になります。図中では、それぞれのツールがIDAの逆アセンブルビュー(IDA View-A)で表示している"_WinMain"関数(正確にはMFCのAfxWinMain関数)のアドレスと同期し、そのアドレスの情報を各々表示しているのが分かります。

図-1 CTO及びCTO Function Listerの起動ボタンと各画面

2.2 暗号化やエンコード/デコードルーチンの検出

マルウェア作者はコンフィグなどのデータや通信に暗号化やエンコードを行い、発見しづらくしていることが多いのは皆さんご存じのことと思います。その際、AESやRC4といった既存の暗号アルゴリズムが使われている場合と、簡易的にxor命令でカスタムエンコーディングが行われている場合があります。明示的にxorでカスタムエンコーディングをしている場合に加え、前述のアルゴリズムを含む多くの既知の暗号アルゴリズムにもxor命令が入っています。またCPUのレジスタ以上に長いデータを暗号処理するためには、必然的にループ構造も必要になります。そのためCTOでは、IDAが認識している関数を全走査し、xor命令を見つけ、それがループの中にあるかをチェックし、結果を一覧するビルトインコマンドがあります。また、該当した関数名がデフォルトから変更されていない場合は、"xorloop_"という接頭辞を付与して改名することで、関数名からも簡単に見つけられるようにもしています。
図-2はそのコマンドの実行方法です。CTOからもショートカットで実行することは可能ですが、ここではCTO Function Lister上のメニューからの実行方法について説明します。

まず、ドロップダウンメニューのボタンをクリックし、そこから、"Built-in scripts"、"Find xor instructions in a loop"を選択します。解析中のプログラムサイズにもよりますが、本マルウェア(コードセクションのサイズが約280KB)であれば、2~3秒程度で終わります。

結果はOutputウィンドウにも表示されますが、CTO Function Lister上で該当関数のみにフィルタして表示することも可能です。それを行うためには図-3のとおり、再度ドロップダウンメニューを開き、"Preset filters"、"xor instruction in a loop"を選択します。

図-2 ループ内のXOR命令検出コマンドの実行方法

図-3 ループ内のXOR命令の検出結果表示方法

これにより、図-4のようにループ内にxor命令がある構造を持つ関数のみが列挙されます。IDAはFLIRTやLumina(注3)により、静的リンクされたC言語などのライブラリ関数の名前をある程度変更してくれます。ご覧いただいて分かるとおり、それらによって最初の2つの関数以外はすべて名前が付いています。よって、優先的に見なければならないのは最初の2つの関数(sub_1025F0とsub_102AB0)です。先ほどのコマンドは該当xor命令上に"CTO-xorloop"というコメントを付与しており、それをCTO Function Lister上でフィルタして表示しています。cmtというサブツリーがそれにあたり、CTO Function Lister上の該当行をクリックすることで、IDAの逆アセンブルビュー上で該当アドレスにジャンプすることができ、周辺のコードを確認することが可能です。

図-4 ループ内のXOR命令検出結果と、その付近のコード

前述のコマンドで得られた2つの関数の周辺コードを確認したところ、1つは悪意のあるサーバからダウンロードしたペイロードを復号する際に使用するルーチン、もう一方は本検体に内蔵されているC&Cサーバのホスト名やIPアドレスなどのコンフィグデータを復号するルーチンでした。このような重要なコードを瞬時に見つけることができます。

今回はxor命令によるカスタムエンコーディングを例に取りましたが、AESなどの暗号アルゴリズムや、SHA256やMD5といったハッシュアルゴリズムは多くの場合、特徴的なmagic value(注4)やテーブルを持っています。そのような特徴を検出するために、findcrypt(注5)やIDA Signsrch(注6)などのサードパーティスクリプトやプラグインが公開されています。CTO Function Listerはこれらの結果も認識し、フィルタして表示することもできます。これらを組み合わせて活用することにより、暗号化/復号ルーチンやエンコード/デコードルーチンを効率良く発見し、迅速にその周辺コードを確認していくことができます。

2.3 パス探査

CTOは該当アドレスへの、もしくはそこからの経路を表示することができます。図-5はCTO Function Listerで発見したxor命令を右クリックして"Find the path(s) to this node"を選択した際の結果です。結果は画面右側にコールツリーグラフとして表示されます。

図-5 パス探査

このグラフは、各関数アドレスやそれを参照するコードやデータの関連性を示してはいるものの、残念ながら純粋な実行パスではありません。なぜかというと、仮にある関数内に関数ポインタがあったとして、それが必ずその場で呼び出されるわけではないからです。例えば、関数ポインタをレジスタやHeapに格納して、かなり先の関数で呼び出すこともあるでしょう。またC++のvftableのような仕組みでは、間接呼び出しが多用されます。正確な実行位置を得るためにはクラスのインスタンスを追いかけ、すべてのアクセスを見つけ、vftableから関数ポインタを取り出して実行する処理を見つけなければならず、コードが非常に複雑になるからです。そのためCTOでは、コードが関数ポインタにアクセスした時点でそのアドレスを抽出して、このような親子関係のグラフを作っています。ただし、それでも十分役に立つものになっています。

この例であれば、パスの最初はdynamic initializerという関数です。この関数は、CRT(C-Runtime)内のinitterm(注7)という関数で処理されます。このマルウェアはMFCを使って記述されていることがコードを読むことで分かります。MFCアプリケーションはメインアプリケーションクラスをグローバル変数として宣言する必要があります。その宣言により、dynamic initializerでカプセル化されたメインアプリケーションクラスのコンストラクタがinittermから呼び出され、そのクラスインスタンスがグローバル変数に格納されます。表示されている関数名はLumina(注8)で自動的に付けられたものであり、for以降は明らかに間違った名称が付けられてしまっています。しかしCTOが表示したパスが示しているとおり、そのコンストラクタ内で、Cselfmake3Appというクラス名のvftableのアクセスが確認できます。またこのクラスがCWinAppクラスを継承していることがClass Informer(注9)の結果の1つである、クラス継承の階層構造からも確認できました。これらの事実から、Cselfmake3Appがこのマルウェアのメインアプリケーションクラスであることは明白です。

次にCselfmake3Appのvftableは、sub_101030という関数と接続しています。CTOは、関数内に存在するグローバル変数へのアクセスを抽出してキャッシュとして持っています。特にその中でも変数名の先頭やそのアドレスに付与されたコメントの最後にvftableやvtableという文字列を発見した場合、そのグローバル変数をvftableとして扱ってテーブルをパースし、一定の法則に従って関数ポインタ群をそのvftableに属するものであると認識します。IDAはRTTIを認識することが可能なため、このアドレスのコメントにvftableを含む文字列が付与されます。よってCTOの初回実行時にvftable解析処理が実行され、CTO内でsub_101030がこのvftableの一部であると認識済みです。そのためvftableに属する関数へのアクセスが発生すると、CTOはこのようにこの関数ポインタを仮想メソッドとして接続できるのです。図-6は、Cselfmake3Appのvftableのノード(上から3番目の「??_7Cselfmake3App@@6B@」)をCTO上でクリックしたときのIDAの画面を表示したものです。「IDA View-A」ではvftableの先頭から関数ポインタが連続していますが、先頭から0x50の位置にsub_101030があることが分かります。ちなみに、32-bitのMFCメインアプリケーションクラスでは、vftableの0x50にInitInstanceという仮想メソッドが存在します。つまり、sub_101030はInitInstanceです。

図-6 MFCメインアプリケーションクラスのvftableとInitInstanceの関数

MFCアプリケーションは前述のとおりCRT内でメインアプリケーションクラスのコンストラクタを処理した後、WinMain関数(厳密にはAfxWinMain)内でInitInstanceやRunなどのいくつかのメソッドを実行します。特にInitInstance関数は、MFCアプリケーションの規約上、必ずOverrideすることが定められており(注10)、ここが実質的にマルウェアのメイン関数となっていることが多いです。このマルウェアもInitInstance(sub_101030)が呼び出されており、その中でマルウェアのコンフィグをデコードするルーチン(sub_102AB0)を呼び出しているのがCTOのコールツリーグラフから簡単に分かります。

また、CTOのパス探査のもう1つの特徴は、クロスリファレンス(注11)さえあれば、グローバル変数(文字列を含む)であってもパスを作成できる点です。IDAにもProximity View(もしくはBrowser)という機能がありますが、関数にしか利用できません。これはCTOを使う利点の1つであると言えます。

注意点として、今回紹介したようにCTO Function Lister上でこの機能を使うためには、CTOをあらかじめ起動しておく必要があることを付け加えておきます。

2.4 std::string/std::wstringの検出

C++で書かれたマルウェアの多くは、std::stringやstd::wstringを文字列操作に使っています。これらのクラスはコンストラクタや一部のメソッドがインライン展開されてしまうため、一見ではこのクラスが使われていることを知るのが困難な場合があります。ただし、クラスレイアウトの初期化を行うコードには特徴的な初期値が使われるため、多少の誤検知はあるものの、単純なパターンマッチングで検出することが可能です。

これらの検出は、先ほども紹介したCTO Function Listerのドロップダウンメニューから"Built-in scripts"、"Find notable instructions"を選択することで行えます。また、フィルタしてコマンドの結果を確認するためには、同じくドロップダウンメニューから"Preset filters"、"Notable instruction"を選択すれば、表示することができます。

例として、マルウェアによってデコードされたコンフィグデータをパースしていくコードで使われていたstd::stringを見ていきます。図-7はCTOで検出したstd::stringの初期化部分のコードを表示したものです。図中1つ目の赤枠では、即値0xfでスタック変数が初期化されているのが分かります。これは、Visual Studioのstd::stringで長年使われている初期化コードの一部です。その2命令下(2つ目の赤枠)には、1バイト分のNULL文字でバッファの先頭部分(前述の0xfで初期化したアドレスより-0x14の位置)を初期化しているコードも見えます。これらがセットで存在した場合、筆者はstd::stringであると判断して構造体を適用するようにしています。

図-7 std::stringの検出

std::stringのクラスレイアウトはドキュメント化されていない内部的なものであり、我々が調べた限りでもVisual Studioのバージョンに応じて数パターンあります。このマルウェアはVisual Studio 2008でコンパイルされたものであることが分かりました。そのため、そのバージョンに対する適切な構造体をロードして、スタック上のstd::stringインスタンスの先頭にその構造体を適用することで、図-8のようにきれいにstd::stringを認識できます。

図-8 std::stringの構造体適用とメンバ変数の認識

2.5 実践 CTO/CTO Function Lister

紹介したIDAプラグインを使ってマルウェア解析をする講義を、2023年2月に行われたGCC 2023 Singapore(注12)で実施してきました。ランダムに選ばれた4~6人で1チームを作り、講義の最後に本稿で示したマルウェア検体の特徴的な機能やコードをCTF形式で出題し、ゲーム感覚で解答してもらいながらマルウェア解析をしてもらいました。

受講生は各国から選抜された学生たちでしたが、IDAの使用やリバースエンジニアリング自体の経験がない人も多く、CTFを始める前に簡単なレクチャーを行いました。それでも、ここで紹介したようなテクニックを駆使して時短をしていくことで、優秀なチームはこのマルウェアであれば1時間半ほどで大部分の解析を終えられるようになりました。また三分の二以上のチームは3時間程度で重要な部分をほぼ解いていました。この講義ではリバースエンジニアリングのみを実施してもらうために実行ファイルそのものは渡さず、そのファイルをロードしたIDAのデータベースのみを渡して解析してもらいました。実行してしまえば、このマルウェアは動作が単純なので簡単に動作概要を把握できますが、コードを読んで正確にマルウェアの挙動を把握する能力も必要であるため、あえて厳しいやり方を受講生に課しています。そのような状況下でも、紹介したツールやテクニックを使って勘所を養うことで、急成長していく様子はとても感動的でした。

2.6 まとめにかえて

CTOやCTO Function Listerは、本稿で紹介した以外にも、過去のマルウェア解析で必要性を感じた機能を実装しています。これからも継続的に自動化などのアイデアを考えて実装していきます。これらのツールが皆さんのマルウェア解析の一助になれば幸いです。

  1. (注1)VB2021 localhostでの発表は次のURLを参照のこと。CTO (Call Tree Overviewer) yet another function call tree viewer(https://vblocalhost.com/conference/presentations/cto-call-tree-overviewer-yet-another-function-call-tree-viewer/)。また、CTOとCTO Function Listerは筆者のgithubリポジトリで公開している(https://github.com/herosi/CTO)。
  2. (注2) IDA Pro(https://hex-rays.com/ida-pro/)はマルウェア解析者にとって必須の逆アセンブラ、デコンパイラ。CTOやCTO Function ListerはIDAPython APIを使って記述をしている。
  3. (注3)FLIRTやLuminaはIDAの機能の一部で、どちらもパターンマッチングを行って既知の関数を検出し、関数名を変更することができる。FLIRTはローカルのデータベースを参照するのに対し、Luminaはクラウド上のデータベースを参照する。Luminaの場合、まずIDA利用者が命名した関数の情報をクラウド上のLuminaサーバに共有しておく。そして同じパターンを持つ関数が、他の利用者が解析中のバイナリ上で発見された場合、Luminaサーバ上の関数名が適用される。
  4. (注4)magic valueとは、あるフォーマットのヘッダやフッタを一意に特定するための目印となる特定の文字列や数値。
  5. (注5)findcrypt(https://github.com/you0708/ida/tree/master/idapython_tools/findcrypt)はIDA Pro用のサードパーティスクリプトで、著名な暗号アルゴリズムやハッシュアルゴリズムが持つテーブルやmagic valueをパターンマッチングにより検出することができる。findcryptには複数の実装があるが、このfindcryptはPythonで記述されており、拡張が容易であるため、筆者はこれを利用している。
  6. (注6)IDA Signsrch(https://sourceforge.net/projects/idasignsrch/)はIDA Pro用のサードパーティプラグインで、findcryptと同様に暗号アルゴリズムやハッシュアルゴリズムを検出するために利用する。findcryptと似たようなツールだが、それぞれ守備範囲が異なる場合があるので、複数実行することがある。
  7. (注7)initterm(https://learn.microsoft.com/cpp/c-runtime-library/reference/initterm-initterm-e)はメイン関数の実行前に、CRT内でグローバルオブジェクトを初期化する関数。CRT内でinitterm関数呼び出し時にグローバル変数を第1、第2引数に取るので、IDAがこの関数を認識できていなくても、比較的見つけやすい。inittermは2つの引数の間にある関数ポインタを順次実行する。各関数ポインタはdynamic initializer(https://learn.microsoft.com/cpp/c-runtime-library/crt-initialization)のコードによってカプセル化されている。そのコード内でグローバルオブジェクトのコンストラクタが実行され、そのクラスインスタンスがグローバル変数に格納される。
  8. (注8)Luminaは前述のとおり、一般利用者が付けた名称が反映されてしまうため、名前の正確性は付けたユーザの技量に依存する。よって、あまり信用できないことが多く、参考程度にするのが良い。今回の例でも不正確な名前が付いている。
  9. (注9)Class InformerはIDA Proのサードパーティプラグインで、C++のRTTI(Runtime Type Information)を解析し、クラス名やクラス継承の階層構造を視認することが可能なツール(https://sourceforge.net/projects/classinformer/)。IDA 7.0からRTTIの解析自体はできているが、階層構造の表示やクラスの検索機能など、まだこのプラグインの方が優れた点があるため、現在でもこれを利用している。また、64-bit版IDAでもPE32上のクラス情報を復元できる改良版を筆者のgithubリポジトリで公開している(https://github.com/herosi/classinformer-ida8)。これは、IDAが8.0から32-bit版IDAを段階的に廃止し、64-bit版のみに移行し始めたためで、オリジナルのClass Informerは64-bit版IDA上でPE32を解析できなかったため、今後の動きをにらんでの処置。
  10. (注10)次のURLにCWinAppを継承した場合にOverride可能なメソッドと、Overrideが必須なメソッドが記述されており、InitInstanceのみが必須となっている(https://learn.microsoft.com/cpp/mfc/overridable-cwinapp-member-functions)。
  11. (注11)クロスリファレンスはxrefsとも呼ばれ、IDAの最も重要な機能の1つ。特定のアドレスを参照するコードやデータを一覧してくれる。参照と被参照(xrefs fromとxrefs to)の2種類があり、IDAはどちらも表示、利用することが可能なのでクロスリファレンスと呼ばれている。CTOもこれを利用して親子関係を作り出している。
  12. (注12)GCC(Global Cybersecurity Camp)(https://gcc.ac/)はアジアを中心に8か国(2023年2月現在)から選抜された学生などへの教育プログラム。2023年で5回目となり、2月にシンガポールで行われ、本文のとおり筆者たちもそこで講義を行った(https://gcc.ac/gcc_2023/lectures/#reverse-engineering-malware-written-in-c-with-ida-and-semi-automated-scripts)。IIJは業界に貢献する目的で、第1回から継続的にトレーニングを提供している。

鈴木 博志

執筆者プロフィール

鈴木 博志 (すずき ひろし)

IIJ セキュリティ本部 セキュリティ情報統括室 マルウェア&フォレンジックアナリスト。
IIJのCSIRTチームであるIIJ-SECTのメンバーであり、社内、顧客のインシデント対応に従事。主にマルウェア解析とフォレンジック調査を担当。そこから得られた知見を元に、Black Hat(USA、Europe、Asia)、Virus Bulletin、FIRST TCなどの国際カンファレンスや、内閣サイバーセキュリティセンター(NISC)、総務省、法務省、IPA、産総研などで講演を行う。また、Black Hat USA、FIRST(Annual、TC)、Global Cybersecurity Camp、MWS、セキュリティキャンプ全国大会やサイバーコロッセオなど、国内外のカンファレンスや教育プログラムでの専門家や学生向けのトレーニング講師も兼ねる。特にBlack Hat USAでは日本人として初めてトレーニング講師に選ばれ、フォレンジック調査とマルウェア解析を使用したインシデントレスポンスに関するトレーニングを提供している。この分野では17年を越える経験を有する。

2. フォーカス・リサーチ(1) CTOとCTO Function Listerを使ったマルウェアの解析方法

ページの終わりです

ページの先頭へ戻る