マルウェア研究者は、複雑で高度に難読化されたサンプルに直面することがよくあります。そして、今日私たちが扱っているマルウェアである GuLoader は典型的な例です。
アセンブリ コードを逆コンパイルして生成されたこの疑似コードを見てください。醜くて判読できません。
このようなパズルに直面すると、途方に暮れてしまうかもしれません。一体どこから始めればいいのでしょうか?このサンプルの分析にはどのように取り組みますか?分解してみましょう。
この記事では、GuLoader を参考にして、そのようなコードの難読化を解除する戦略を検討します。以下について学びます:
この記事は、ANY.RUN によって以前に公開されたGuLoader マルウェア分析に基づいています。私たちのブログにアクセスして、分析するサンプル、解凍手順、およびこれから説明する内容の多くを部分的に自動化する Ghidra スクリプトを見つけてください。
この記事では静的解析に焦点を当てます。ただし、GuLoader サンプルを動的に分析したい場合は、 ANY.RUNクラウド マルウェア サンドボックスを使用できます。
ビジネス用メールを使用して、Enterprise プランの14 日間の無料トライアルに申し込みます。 VM インスタンスの延長された有効期間、無制限のタスク、Windows バージョン 7 ~ 11 を活用してください。
動的分析を使用すると、マルウェアの動作を技術的な設定に関連付けることで、マルウェアが現実世界でどのように動作するかを確認できます。これは、さまざまなシステム設定全体でその動作を確認し、IOC を収集する方法です。
これ以上遅らせることなく、飛び込んでみましょう。
サンプルを解凍した後、シェルコードの手動分析を開始しますが、難読化されていることがすぐにわかります。コードを調べると、GuLoader が採用する難読化手法を次のカテゴリにグループ化できます。
これは、立ち止まって、使用されている難読化手法を分析し、難読化解除戦略を開発する良い機会です。
たとえば、私たちの場合、これらの手法のほとんどは、最終的な実行結果を変更しないコードを導入しています。したがって、多くの場合、読みやすさを向上させるために安全に「NOP」することができます。ただし、注意して作業を進めてください。すぐにわかるように、難読化されたコードのすべてがプログラムの動作に無関係であるわけではありません。
ここで、これらの難読化テクニックを個別に調べて、それらを破る方法を見てみましょう。
コードには多くの XMM 命令が散在しています。これらは無秩序に見え、分析プロセスが複雑になります。解凍されたシェルコードの最初のバイトからすぐにそれらに遭遇します。
多くのエミュレーション エンジンは、デフォルトのサポートがないため、これらにつまずくことに注意してください。私たちはAngr、Triton、Ghidraの組み込みエンジンをテストしましたが、すべて不十分でした。
GuLoader の場合、XMM 命令は実際にはコードの意図された動作に影響を与えません。多くのマルウェアで同様の難読化手法が使用されています。したがって、次の表に示すように、すべての XMM 命令を「NOP」に安全に置き換えることができます。
Ghidra 逆アセンブラでの結果は次のようになります。
無条件の JMP 命令は、コードをより小さなチャンクに分割します。この方法は、ウイルス対策ソフトウェアやその他のセキュリティ ツールによる検出を回避するためによく使用されます。さらに、特に大量のコードを扱う場合、アナリストはこれらのブロック間を移動する必要があるため、アナリストの仕事がより時間がかかり、イライラする可能性があります。 GuLoader やその他のマルウェアは一般にこの手法を使用します。
この難読化方法は非常に簡単に破られます。逆コンパイルされたコード内の逆アセンブラは、多くの場合、これらのブロックを正常に連結し、無条件ジャンプが存在する場合でも、コードの可読性を向上させます。そのため、小さなブロックをマージすることなくそのままにしておくことができます。
ジャンクアセンブリ命令は、多くの場合、難読化の追加レイヤーとして機能します。これらの命令は具体的な機能を実行せず、通常、レジスタ値、実行フロー、またはメモリは変更されません。
これらは GuLoader 内でも同様に発生します。
アクションを実行しない命令 (「NOP」、「FNOP」) とゼロビットでシフトまたは回転する命令 (「SHL reg, 0」、「ROL reg, 0」) に注意してください。 「OR reg, 0」、「XOR reg, 0」、「CLD」、「WAIT」などの他の影響のない命令も存在します。
偽の比較命令に対処することは、単にジャンクの比較命令を「NOP」に置き換えるだけよりも困難です。一部の比較命令は正しいコード機能に必要であるため、すべての比較命令を削除することはできません。これに取り組む 1 つの方法は、遭遇するすべての比較命令を「マーク」することです。比較の結果を使用する命令が見つからない場合は、NOP しても安全です。条件付きジャンプなどを見つけた場合は、削除を避けるために比較のマークを外します。
次の表は、「CMP EDX,0x0」を除くすべての比較命令が選択的に NOP に置き換えられた例を示しています。
GuLoader は、偽の「PUSHAD」命令と一致する「POPAD」命令を組み合わせて使用する難読化戦術も採用しています。一時的にレジスタ値を変更することはできますが、元のレジスタ値に戻す「POPAD」によって無効化されます。
私たちの調査によると、GuLoader のすべての「PUSHAD」命令は無関係であることがわかりました。そこで、「PUSHAD」、「POPAD」、および中間命令を NOP に置き換えることでこの問題に対処します。
ただし、GuLoader のすべての「POPAD」命令がジャンクであるわけではありません。対応する「PUSHAD」がないものはそのままにしておきます。
前のものと同様のもう 1 つの難読化手法は、偽の「PUSH」命令の使用です。これらの命令は値をスタックにプッシュしますが、それはただちにポップオフされます。
例としては、「PUSH SS」命令が含まれており、その後にレジスターまたはメモリー位置を変更する命令が続く場合があります。ただし、その後の「POP SS」によりスタックポインタは初期値に戻されます。
偽の PUSH 命令の無効化は偽の PUSHAD のプロセスに似ていますが、プッシュされていないレジスタを変更しないことが重要です。
不透明な述語は、常に true または false を返す条件付きステートメントですが、分析や予測が困難です。これらは GuLoader のコードに含まれており、ロジックの理解を複雑にしています。
たとえば、「MOV BL, 0xB6」と「CMP BL, 0xB6」のような 1 対の命令の後に、「JNZ ADDR」のような条件付きジャンプが続く可能性があります。比較された値は移動された値と等しいため、比較では常に false が返され、ジャンプが不要になり、不可解になります。
ジャンプ条件を「予測」する必要があるため、不透明な述語を克服するのは難しいように思えるかもしれません。ただし、すべての不透明な述語が「PUSHAD」ブロックと「POPAD」ブロック内に収まるため、状況はより単純になります。したがって、これらの命令間のすべての述語を単純に NOP に置き換えます。
難読化された算術式は、GuLoader で使用される興味深い技術の 1 つです。これらにより、実行される実際の操作を理解することが難しくなります。これらの式には、加算や減算などの算術演算が組み込まれています。場合によっては、偽の比較、不透明な述語、ジャンク命令などの他の難読化と混合されることがあります。
一例として、定数値をレジスタに移動し、算術演算を実行します。
別のインスタンスは、定数値をスタックにプッシュし、メモリ上で計算を実行します。
GuLoader の算術式の難読化を解除するために、偽の比較を処理するのと同様のアプローチを採用します。 2 番目の引数がスカラー値であるすべての「MOV」命令と、引数がスカラーであるすべての「PUSH」命令にマークを付けます。算術演算が発生すると、最初の命令の定数値を更新し、現在の定数値を NOP に置き換えます。このようにして、最初の命令に結果が与えられ、後続の算術命令は NOP に置き換えられます。
以下は、最適化された「MOV」および「PUSH」操作の例です。
オペランドのサイズには注意してください。操作中に正しいサイズを維持することが重要です。
ANY.RUNの専門家によるこの記事では、典型的な難読化解除戦略を強調するために、実際の例として GuLoader を使用しました。私たちはコードの分析から始め、次に同様の難読化戦術をグループ化し、最後にそれぞれに取り組む独自の方法を考え出しました。
ただし、これらの手法は万能の解決策ではないことを理解することが重要です。各マルウェア サンプルには、独自の対策が必要な独自の難読化戦略が示されている場合があります。
私たちの議論は、難読化解除のような複雑なタスクを明確で管理可能なステップに分割することの重要性を強調しています。マルウェアの難読化解除を成功させるには、詳細な分析、難読化パターンの特定、最適化された戦略の作成が必要であることに注意してください。