This post is also available in: English (英語)

概要

マルウェア作成者は解析対策技術をいくつもコードに埋め込んで人間のアナリストやサンドボックス分析プロセスを邪魔してきますが、防御側にもそうした技術を打ち破る技術はあります。本稿は特徴的な解析対策技術を2点採用しているマルウェアをより高速に解析するための技術を2つご紹介します。1つめの技術は「API関数のハッシュ化」というよく知られた技で、どの関数が呼び出されたかを難読化します。2つめの技術は「不透明述語 (opaque predicate)」で、これは制御フローの難読化に使われます。

本稿で紹介するスクリプトは、BazarLoaderや、似たような解析対策技術を使うべつのマルウェアファミリにも使えます。ここでは参考として、最近私たちがリバースエンジニアリングツールのIDA ProでBazarLoaderを解析したさいに作成したIDAPythonスクリプトを紹介し、この手の解析対策技術を破ってみたいと思います。なお、BazarLoaderというのは、さまざまなランサムウェアグループが使っているWindowsバックドアです。

パロアルトネットワークスのお客様は、Cortex XDR またはWildFire脅威防御セキュリティサブスクリプションを有効にした次世代ファイアウォールにより、同様の解析対策技術を使用するマルウェアファミリから保護されています。

本稿で解説する主なマルウェア BazarLoader
Unit 42の関連トピック Malware, anti-analysis techniques

目次

マルウェアコードを再利用してAPI呼び出しの難読化を破る
「不透明述語」の除去を自動化する
マルウェアアナリスト対マルウェア作成者
IoC
追加リソース

マルウェアコードを再利用してAPI呼び出しの難読化を破る

ネイティブファイルとしてコンパイルされたマルウェアが悪意のある振る舞いを実行するには、WindowsのAPI関数を呼び出す必要があります。そのためにどのAPI関数を使うかについての情報は、通常はネイティブファイル内のIAT(インポートアドレステーブル)に格納されています。したがってこのテーブルは「そのマルウェアが何をしようとしているのか」を知る分析プロセスを始めるのにうってつけの場所です。

このデモをするのにあたり、私たちは最近検出したBazarLoaderのサンプルに目をつけました。そのBazarLoaderサンプルからパッカーの層を剥がしてみると、IATがないことがわかりました(図1参照)。またほかのマルウェアで時おり見られる手法である「実行中に構築されるIAT」も存在しませんでした。BazarLoaderは関数呼び出しを難読化して解析しづらくし、またIATの読み取りに頼る検出技術を回避しています。

BazarLoaderは関数呼び出しを難読化して解析しづらくし、またIATの読み取りに頼る検出技術を回避しています。
図1. CFF Explorerで見たBazarLoaderにはIATが存在しない

実際、BazarLoaderは実行時に個別に呼び出すことですべてのAPI関数を解決しています。関数が実行中に解決されることがわかった後、次の関数が300回以上参照されていることに注目しました。

ここに示すように、BazarLoaderは実行時に個別に呼び出すことですべてのAPI関数を解決している
図2. 難読化されたWindows API関数を解決する関数(黄色でハイライト表示)

多くのマルウェアではよく知られているハッシュアルゴリズムで関数のアドレスを解決していますが、BazarLoaderの使うハッシュアルゴリズムはユニークです。API関数の解決プロシージャ(sub_18000B9B0FN_API_Decoderのラベルが付けられている)は、3つのパラメータを必要とし、要求された関数のアドレスを返します。

ここでFN_API_Decoderで使われているアルゴリズムをリバースエンジニアリングしてPythonで再実装すれば、すべての関数を解決できます。ただこれでは時間がかかりすぎるし、マルウェアの使うハッシュアルゴリズムが異なれば、そのつど全プロセスを繰り返さなければなりません。

そこで私たちはハッシュアルゴリズムから独立したアプローチを取ることにしました。ハッシュ関数そのものを利用するのです。そのために、IDA ProのIDAPythonがもつAppcallという機能を使い、FN_API_Decoderを呼び出して、これに必要なパラメータを渡しました。Appcallの結果はWindows API関数の解決済みアドレスとなります。マルウェアのデバッグに使ったAppcall機能を使えば、サンプル内の任意の関数を組み込み関数のように実行できます。

以下のコードを使うと、マルウェアのプロセスをデバッグしつつFN_API_Decoderを実行してWindows API関数のアドレスを解決できます。

このコードを使うと、マルウェアのプロセスをデバッグしつつFN_API_Decoderを実行してWindows API関数のアドレスを解決できます。
図3. IDAPythonでAppcallを使う

次にFN_API_Decoderへのクロスリファレンスをすべて調べ、必要となるパラメータをぜんぶ集めました。以下のコードは、APIの関数呼び出し解決に必要なパラメータを検索・抽出するものです。

このコードは、APIの関数呼び出し解決に必要なパラメータを検索・抽出するものです。
図4. 3つのパラメータを検索・抽出するIDAPythonのコード

最後に、Appcallから返された値を使えば、APIへの動的呼び出しをすべて対応する名前に変更し、コメントもつけられます。

Appcallから返された値を使えば、APIへの動的呼び出しをすべて対応する名前に変更し、コメントもつけられます。
図5. IDAPythonのコードで動的呼び出しを検索

以上の手順をまとめて、APIの関数呼び出しの難読化を解除できました。

ここに示すように、APIの関数呼び出しの難読化を解除することで、解析対策技術に対応しました。
図6. 上記IDAPythonスクリプトの実行前
API関数呼び出しの難読化解除の第2段階(名前を変更してコメントを追加)
図7 API 関数呼び出しの名前を変更してコメントを追加

API関数呼び出しの名前をすべて変更してみると、マルウェアに含まれているほかの興味深い関数が簡単に見つかるようになりました。たとえばsub_1800155E0はBazarLoaderでコードインジェクションを行うプロシージャです。

API関数呼び出しの名前をすべて変更してみると、マルウェアに含まれているほかの興味深い関数が簡単に見つかるようになりました。たとえばsub_1800155E0はBazarLoaderでコードインジェクションを行うプロシージャです。
図8 API呼び出しの名前の変更前
ここに示したように、解析対策技術にはAPI呼び出しの難読化が含まれています。これらはコードインジェクションに関連するAPIであるとラベルを付けられています。
図9 難読化を解除され、コードインジェクション関連のAPIであるとしてラベル付けされたAPI呼び出し

これらのIDAPythonスクリプトのおかげでこのBazarLoaderサンプルがどのような機能を含んでいるかをすばやく評価できるようになりました。

「不透明述語」の除去を自動化する

BazarLoaderには、リバースエンジニアリングツールからの保護のためにOP(Opaque Predicate、不透明述語)が使われています。OPは実行時につねにtrueまたはfalseに評価される式のことで、マルウェア作成者はこうした複数のOPを実行されることのないコードブロックとともに追加してコードを複雑化にすることで、静的解析ツールが処理しづらいようにしています。

以下の逆アセンブル済みコードはBazarloaderのOPの1つを示したものです。

ここに示す逆アセンブル済みコードは、BazarLoaderがリバースエンジニアリングツールからの保護に使うOPの1つで、本稿で解説する一般的な解析対策技術の1つです。
図10 BazarLoaderのOPの一例

上記の制御フローグラフ(CFG)では、コードフローが無限ループに陥ることはありません(図10赤で示したコードブロック)。つまり上記のOPを評価することにより無限ループを回避しています。

こうしたOPがマルウェアアナリストにもたらす困難がどのようなものかをお見せしましょう。以下のCFGは、サンプル中の小さな関数の1つ(sub_18000F640)に含まれる実行されることのないコードブロック(図11 赤で示すコードブロック)を示したものです。

制御フローグラフ内の赤で示したブロックは、サンプルに含まれる小さな関数の1つ内の実行されることのないコードブロックを示したものです。
図11 BazarLoaderのsub_18000F640関数。実行されることのないコードブロックが赤色で表示されている

サンプル内の各関数を解析しつつ実行されないコードブロックを手で修正することもできなくはないですが、時間もかかるしあまり現実的ではありません。そこで、この処理を自動化するというもっとスマートな方法を選ぶことにします。

まず、すべてのOPの位置を特定する必要があります。いちばん一般的なのはIDA ProのバイナリサーチのメカニズムでOPの全バイトシーケンスを見つける方法です。ただこの方法は、コンパイラがマルウェアサンプルのビルド時にOPを生成したらしいことから、使えないことがわかりました。OPのバリエーションが多すぎてバイトシーケンスでは対応しきれないのです。

また、私たちは、OPの位置を特定するだけでなく、マルウェアサンプルが実行しないコードブロックの回避を決める正確なポイントも知る必要がありました。

そこで以下のコードで、関数内のOPの位置を特定します。

私たちは、OPの位置を特定するだけでなく、マルウェアサンプルが実行しないコードブロックの回避を決める正確なポイントも知る必要がありました。ここで紹介するコードで、関数内でのOPの位置を特定できます。
図12. 関数内のOPを検索するIDAPythonコード

次に、OPの命令を修正し、実行されないコードブロックから強制的にコードのフローを引き離す必要があります。

そこで私たちは、以下のコードで関数内のOPを修正しています。

ここで示すコードは、OPの命令を修正し、実行されないコードブロックから強制的にコードのフローを引き離しています。
図13. OPを修正するIDAPythonコード

これらのOPはHexRaysデコンパイラの出力もいじっています。OP修正前の関数(sub_18000F640)はこんな内容でした。

この図はOP修正前の関数(sub_18000F640)の様子を示しています。
図14 逆コンパイルされた関数 sub_18000F640

上記2つの技術を適用した結果、読みやすくわかりやすい擬似コードに逆コンパイルできました。

すべてのOPを修正して難読化されたAPI呼び出しの名前を変更してみると、この関数(sub_18000F640)がGetModuleFileNameW()の単なるラッパー関数であることが分かるようになりました。

上記2つの技術を適用して解析対策技術を破ってみると、読みやすくてわかりやすい擬似コードに逆コンパイルできました。
図15. OP削除後に逆コンパイルされた関数 sub_18000F640

マルウェアアナリスト対マルウェア作成者

マルウェアの作成者たちは、解析にかかる時間やリソースを増やしてやろうと、解析対策技術を盛り込んでくることが少なくありません。こうした解析対策技術を破る方法を示したBazarLoader用の上記スクリプトスニペットを使えば、似たような技術を使うほかのマルウェアファミリサンプルの分析に必要な時間を短縮できます。

パロアルトネットワークスのお客様は、Cortex XDRまたはWildFireと脅威防御セキュリティサブスクリプションを有効にした次世代ファイアウォールにより、同様の解析対策技術を使用するマルウェアファミリから保護されています。

IoC

BazarLoaderのサンプル: ce5ee2fd8aa4acda24baf6221b5de66220172da0eb312705936adc5b164cc052

追加リソース

難読化されたAPI呼び出しの名前変更・解決を行う完全なIDAPythonスクリプトはこちらのGitHubで公開しています。

関数内の不透明述語を検索して修正する完全なIDAPythonスクリプトはこちらのGitHubで公開しています。

Enlarged Image