なぜ、「マルウェアの筆跡鑑定」などといったものをやろうと思ったのか?
私の場合、マルウェア解析は動的解析やアーティファクトの抽出より、静的解析・デバッガを使った解析でプログラムの作りや構造を解析するといったことを好んでやります。
この方法では、一つの検体の解析に相当の時間をかけてしまうことになる一方で、「根本的な仕組み」が見えてくるので、その「仕組み」の弱点を見切れれば、長く利用できる対策もみつかるんじゃないかなぁと妄想したりしています。
さて、そんな解析方法で解析していると、「コイツ、アホだなー」とか、「このロジックはよく考えて作ってあるなー」といった感想を持つことが多いです。
これは、元来自分はサイバーセキュリティをずっと勉強・研究してきたのではなく、いわゆるSE・プログラマーと呼ばれる仕事に長く携わってきたことに由来しているんだと思います。
仕事柄、自分でプログラムを書くだけでなく、他人のプログラムを読むことも多かったので、「このプログラム作った人は結構色々な知識がある」と思ったり、「このプログラムを作った人はムラや抜け漏れが多く、スキルが低い」と思ったりと、様々な感想を持つことになりました。
そういった感想を持つ根拠は、プログラムに実装されているコードの構造、判定処理などに表れてくるため、自身の経験と照らして優劣を判断している、というわけです。
その後フォレンジック屋さんとなり、サイバー攻撃の調査に関心を示して勉強していたのですが、そんな折の2018年の冬季オリンピックの際に「偽旗作戦」と呼ばれるマルウェアが発見されました。
「OlympicDestroyer」と呼ばれるマルウェアがオリンピックの関係のシステムを複数破壊し起動できなくし、混乱を起こしたのですが、その時最初は北朝鮮の「Lazarus」によるもの、という説が出ました。
理由は、「Lazarus」が使う「BlueNoroff」と呼ばれるマルウェアと類似点が多い、という理由でした。
しかし、これは「Rich Header」と「OlympicDestroyer」の実装の食い違いから偽装が疑われ、後に別の組織が身元を隠すための「偽旗作戦」だったと判明したわけです。
The devil’s in the Rich header
私も「BlueNoroff」と「OlympicDestroyer」をそれぞれ見比べてみたのですが・・・。
アセンブラ言語レベルでみると、これがまあ似ても似つかないワケですわ。
むしろ、頑張らないと「似てる」という場所が見つからないレベル。
ただ、それを実際に比較した資料をつくって発表してみたところ、「そういう分析方法でやった例は聞いたことがない」と言われました。
だってまあ、ただ読んで比較するだけの単純な方法だし、そのくせ手法的に面倒くさく効率が悪いってのはあります。
自分としてはマルウェアの解析の練習にはなったからいいのですが、この解析のときに、こんな感想が生まれました。
「どちらかというと、BlueNoroffを作った人のほうがプログラムのスキルは上。また、BlueNoroffを作った人はアンチフォレンジックについてとても詳しい。一方、OlympicDestroyerはその模倣が不十分で別人だと分かるレベル。ただし、アンチウィルス等がどのようにマルウェアを分類しているかはよく勉強しているようにみえる。」
これは、あくまで私の感想だったわけですが、じゃあ「なぜそう思ったのか?」と振り返ってみると、色々な技術的な点から説明ができたというわけです。
そして、マルウェアの比較の際に同様の視点を持って分析すれば、「マルウェアの作成者が同じか違うかが分かるようになるのではないか」と考えました。
また、コードの特徴を押さえれば、「仮に同じ作者がまったく別の目的のマルウェアを作っても、その特徴が出るのではないか」と考えました。
これをちゃんと系統として整理し、方法論を示し、実証してみれば、一つの研究成果になるんじゃね?と思ってはじめた次第です。
根本的な考え方
現在のマルウェアは主に人が書いているとして、人がプログラムを書くと以下のような特徴がでると考えます。
- プログラム作成者の「知識」や「経験」がでる。
- プログラム作成者の「性格」や「考え方」がでる。
- プログラム作成者の「趣味」がでる。
ある程度の複雑さのプログラムになると、インプット・アウトプットは全く同じ処理であっても、書く人によって中身に差がでます。
また、特にある程度以上の練度になってくると、「自分のパターン」というものができてきます。
これらが、プログラムを作る度にそれぞれのプログラムに含まれていくんじゃないか、と考えたわけです。
これを達成するためには、「観点」を設けてプログラムを分析し、比較する必要があります。
そこで、その「観点」を探すことも研究の対象としました。
先に述べた「BlueNoroff」と「OlympicDestroyer」のほかに、「Emotet」の2020年春ごろと秋ごろの検体の分析をしてみたところ、面白い結果がでました。
これらの詳しい内容、実証例を論文に書いていこうかと思っているところです。
memsetの例
論文に書こうと思っていることを書くと膨大になるので、一例を挙げてみます。
「memsetはどのように実装されているか?」というケースです。
「いや、memsetって言ってるやないかーい!!」ってことなんですが(汗)。
じつは、コレ結構違ったんですよ・・・。
BlueNoroffのmemset
OlympicDestroyerのmemset
Emotet 2020年春バージョンの32ビットコード(パッカー部)のmemset
Emotet 2020年春バージョンの64ビットコードのmemset
Emotet 2020年秋バージョンのmemset
なあ?結構違うだろう?
これらの処理は、メモリを0クリアするために利用されていました。
たかだか、メモリを0クリアするだけでも、こんなに差がでるわけです。
memsetを使うパターンばかりかと思いきや、意外にもこんなところでバリエーションがありました。
「メモリの0クリア」であれば、WindowsならRtlZeroMemoryとかその辺を使うパターンもあると思います。
一方、Emotet2020年春バージョンは、32ビット、64ビットでアーキテクチャが違いますが、処理は激似だということが分かります。
相違点は、ループ回数のカウントが加算か減算かという点と、書き込み先アドレスの指定がオフセット値を利用しているかどうかという点です。
しかし、引数の有効性の判定や返り値が同じことから、これはアーキテクチャ(32ビットと64ビット)とコンパイラの違いの影響の範囲内ではないかと推測します。
「memsetを使うケースが多いのでは」と言われれば確かにその通りで、memsetを使っているケースではあまり大きな特徴とは言えません。
しかし、返して言えば、memset以外を使っているケースは「特徴量として大きくなるのではないか」と思います。
例えば、個人的にはBlueNoroffのmemset処理が大分スマートでお気に入りなのですが、この実装方法は他と比べてもコンパクトで特徴的なので、他のマルウェアでも同じ手口を見つけたら、同じ人じゃないかとちょっと疑いたくなりません?
いや、私はなるんだよwww・・・っていうことで研究しているのです。
また、この実装している人は、他と比べてもスキル高いのかもな、と考えたりもするわけです。
ソースコードの差の影響をうけるのか?
悩ましい点として、「ソースコードが違うから出力されているコードが違う」とは、必ずしも言い切れないのではないか、という点です。
これはまだほとんど検証できていませんが、「最適化の設定などで出力が変わるのではないか?」ということは、容易に想像できます。
そのため、BlueNoroffとOlympicDestroyerのケースの際に、C++コードを実際にコンパイルしてみて結果がどうなるかを検証してみた結果を紹介します。
C++によるメモリクリア処理とアセンブラコード
*VisualStudio 2010でデフォルトのコンパイルオプションを使用
結構予想外の結果になりました。
一番上のfor文で1バイトずつ設定する処理は、Emotet2020年春バージョンの32ビットコードみたいなループになるかと思ったら、最適化君が頑張ってmemsetに置き換えちゃいました。
(・・・じゃあEmotet2020年春バージョンの32ビット、64ビットコードはアノmemsetどうやって実装したんだよwって思うわけですけど。)
中央の4バイトの型のポインタを使って設定するようにしたところ、「rep stosd」を使うようになり、BlueNoroffに近い感じになりました。
一番下はmemset関数を使った例ですが、これはそのままmemsetになりました(あたりまえ)。
この検証結果を見ると、OlympicDestroyerはmemsetを使っていたので一番上か一番下のコードが書かれ、BlueNoroffは中央のコードに似たコード(もう少し複雑)またはアセンブラで直接記述をしたのではないか、と推測できるのではないかと思います。
つまり、少なくともBlueNoroffとOlympicDestroyerは、メモリの0クリアのためのmemset処理のソースコードが異なる可能性が高いことが分かります。
これは、これらのマルウェアの作者が異なる可能性を示す一つの指標になるのではないかと考えているのです。
もちろん、これ一つだけでは、ソース流用したときにたまたま処理に変更が入ったという可能性が考えられます。
しかし、同様の検証を、例えばmemcpyなどの他の複数の処理に対してもやっていき、それらに類似点があるか、もしくは全く違うか、という観点で洗い出していけば、類似性が高いか低いかを判定できるのではないかと考えています。
つまり、類似点のANDが多く取れれば、マルウェアの作者が同じ可能性が高くなる、という考え方です。
今後の課題
こんな単純な思い付きではあるものの、こういった解析・分析をしているドキュメントを見たことが無く。
意外とみんなこんなやり方はしていないのかな、と思いました。
(まあ、原始的でメンド臭い方法だし、普通はやらんわなぁw)
しかし、これをちゃんと理論的な仮説を立て、方法論を考え、それを実証してみれば、一つの方法論くらいにはなるかなと考えています。
これを論文化するには、これを漠然としたものではなく、理論として解説できるようにする必要があります。
これについては、徐々に進めていて、特にEmotetの解析が一通り目途がついた頃からこちらにとりかかっています。
(それゆえに、ブログを書いているヒマもネタもなかった。)
類似した研究の探索も課題です。
お手伝いをしてくださっている大学教授も、「ちょっとこういった方法をやっているのは聞いたことが無い」とおっしゃられており、こんなアホみたいな力技をやってる人がいないのかもしれません。
そういった研究なども参考にしたいのですが、変な方向に尖ったがゆえに類似研究が見つからないという状況です。
論文を書く場合には、オリジナリティを求められる一方で、参考文献もある程度出す必要もあるのですが、コレがマジでないんですよ。
(Sachielトンデモ理論なので)
コードの違いがソースの違いだと必ずしも言えない、という点の研究も課題です。
コンパイルオプション等の設定の変更で、同じソースでも出力されるアセンブラコードが全く違う可能性は否定できず、この点の調査も今後必要になるかもしれません。
例えば、memset() でコーディングしたが、最適化オプションによってはEmotet2020年春バージョンのようなループ処理になるのかもしれません。
もちろん、「そういった最適化オプションを使う傾向がある」という一つのプロファイルにはなると思いますが、やはりコードの類似性の判定には大きな影響がでます。
そういう意味では、「引数のチェックをしている」、「返り値の意味が違う」、「エラー判定の有無やエラー時の処理」といった点の相違は、コンパイラオプションの影響とは関係ないため、コンパイラの動作の影響を受けない処理の差異も着眼点にすることでカバーできるかなと思っています。
さーて、ここまで「Sachielのボクが考えたマルウェア解析トンデモ理論」を読んだ方がどれだけいるか分かりませんがw
(普通、途中で鼻で笑ってそっ閉じすると思うw)
この方法に、価値を持たせることができるのか。
ちょっとした正念場に来ています。
凡人の私には、これでも人生で一大行事になっているんですよw
願わくば、一縷の価値が見いだされんことを。






