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は実行時に個別に呼び出すことですべてのAPI関数を解決しています。関数が実行中に解決されることがわかった後、次の関数が300回以上参照されていることに注目しました。
多くのマルウェアではよく知られているハッシュアルゴリズムで関数のアドレスを解決していますが、BazarLoaderの使うハッシュアルゴリズムはユニークです。API関数の解決プロシージャ(sub_18000B9B0。FN_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へのクロスリファレンスをすべて調べ、必要となるパラメータをぜんぶ集めました。以下のコードは、APIの関数呼び出し解決に必要なパラメータを検索・抽出するものです。
最後に、Appcallから返された値を使えば、APIへの動的呼び出しをすべて対応する名前に変更し、コメントもつけられます。
以上の手順をまとめて、APIの関数呼び出しの難読化を解除できました。
API関数呼び出しの名前をすべて変更してみると、マルウェアに含まれているほかの興味深い関数が簡単に見つかるようになりました。たとえばsub_1800155E0はBazarLoaderでコードインジェクションを行うプロシージャです。
これらのIDAPythonスクリプトのおかげでこのBazarLoaderサンプルがどのような機能を含んでいるかをすばやく評価できるようになりました。
「不透明述語」の除去を自動化する
BazarLoaderには、リバースエンジニアリングツールからの保護のためにOP(Opaque Predicate、不透明述語)が使われています。OPは実行時につねにtrueまたはfalseに評価される式のことで、マルウェア作成者はこうした複数のOPを実行されることのないコードブロックとともに追加してコードを複雑化にすることで、静的解析ツールが処理しづらいようにしています。
以下の逆アセンブル済みコードはBazarloaderのOPの1つを示したものです。
上記の制御フローグラフ(CFG)では、コードフローが無限ループに陥ることはありません(図10赤で示したコードブロック)。つまり上記のOPを評価することにより無限ループを回避しています。
こうしたOPがマルウェアアナリストにもたらす困難がどのようなものかをお見せしましょう。以下のCFGは、サンプル中の小さな関数の1つ(sub_18000F640)に含まれる実行されることのないコードブロック(図11 赤で示すコードブロック)を示したものです。
サンプル内の各関数を解析しつつ実行されないコードブロックを手で修正することもできなくはないですが、時間もかかるしあまり現実的ではありません。そこで、この処理を自動化するというもっとスマートな方法を選ぶことにします。
まず、すべてのOPの位置を特定する必要があります。いちばん一般的なのはIDA ProのバイナリサーチのメカニズムでOPの全バイトシーケンスを見つける方法です。ただこの方法は、コンパイラがマルウェアサンプルのビルド時にOPを生成したらしいことから、使えないことがわかりました。OPのバリエーションが多すぎてバイトシーケンスでは対応しきれないのです。
また、私たちは、OPの位置を特定するだけでなく、マルウェアサンプルが実行しないコードブロックの回避を決める正確なポイントも知る必要がありました。
そこで以下のコードで、関数内のOPの位置を特定します。
次に、OPの命令を修正し、実行されないコードブロックから強制的にコードのフローを引き離す必要があります。
そこで私たちは、以下のコードで関数内のOPを修正しています。
これらのOPはHexRaysデコンパイラの出力もいじっています。OP修正前の関数(sub_18000F640)はこんな内容でした。
上記2つの技術を適用した結果、読みやすくわかりやすい擬似コードに逆コンパイルできました。
すべてのOPを修正して難読化されたAPI呼び出しの名前を変更してみると、この関数(sub_18000F640)がGetModuleFileNameW()の単なるラッパー関数であることが分かるようになりました。
マルウェアアナリスト対マルウェア作成者
マルウェアの作成者たちは、解析にかかる時間やリソースを増やしてやろうと、解析対策技術を盛り込んでくることが少なくありません。こうした解析対策技術を破る方法を示したBazarLoader用の上記スクリプトスニペットを使えば、似たような技術を使うほかのマルウェアファミリサンプルの分析に必要な時間を短縮できます。
パロアルトネットワークスのお客様は、Cortex XDRまたはWildFireと脅威防御セキュリティサブスクリプションを有効にした次世代ファイアウォールにより、同様の解析対策技術を使用するマルウェアファミリから保護されています。
IoC
BazarLoaderのサンプル: ce5ee2fd8aa4acda24baf6221b5de66220172da0eb312705936adc5b164cc052
追加リソース
難読化されたAPI呼び出しの名前変更・解決を行う完全なIDAPythonスクリプトはこちらのGitHubで公開しています。
関数内の不透明述語を検索して修正する完全なIDAPythonスクリプトはこちらのGitHubで公開しています。