マルウェア分析手法:Ring3でドライバーを展開する

トロイの木馬やマルウェアの分析における日常的な作業では、十分な周期性があるため、暗号化されたドライバーをドロップするインスタンスがあります。 しかし、私はやや怠け者で、ring3での作業に慣れているので、低レベルデバッガーに頼らずにドライバーを展開する1つの方法を紹介します。





エントリー


そもそも、この方法は、カーネルお​​よびシステムライブラリから少数のAPIをインポートするドライバーに適していると言う価値があります。 数十個の関数の順序。 そうでなければ、私たちはかなり退屈な仕事を待っていますが、それは価値がありません。



ドライバーの準備


ドライバー自体の構造は、通常の動的ライブラリとほぼ同じです。 そして、それをring3にロードするには、PEヘッダーのいくつかのフィールドを変更する必要があります。



次に、システムの低レベルライブラリからドライバを解放する必要があります。 これを行うために、必要な関数のスタブを使用して独自のdll(または、必要に応じて複数のdll)を作成します。 インポートされたAPIのリストを開き、MSDNまたはその他の情報リソースでプロトタイプを探します。

私の場合、リストは次のとおりです。



パッチ適用前のAPIリスト

ドライバーがシステムのDLLではなく、正確にdllをロードできるように、インポートされたモジュールの名前を私たちが準備したものにパッチします。



パッチ後のAPIリスト

これで、OllyDbgなどのユーザーモードデバッガーにドライバーを読み込むことができます。



完了時に問題を解決して解決する


この場合、パッカーはプリミティブです-xorおよびLZアルゴリズムのバリエーション。 少し散らかっていても、xor復号化プログラムは検討しません。 復号化後、次のコードに到達します。



XORデクリプター後のコード

これが最初の問題です。 コードを手放すだけの場合は、すぐに例外ACCESS_VIOLATIONをキャッチします。 コードは、サービス構造からntoskrnl.exe内のアドレスを取得し、ImageBaseモジュールを見つけます。 しかし、私たちはring3にいるので、FSセグメントにある構造は核構造とは異なります。 そして、コードをコーディングする場合、fsから:[38] 0が考慮され、次のコマンドは0 + 4で読み取られます。 当然、メモリにもntoskrnlもありません。そのため、ntdllアドレスを回避すると仮定します(そのAPIのほとんどはカーネル関数と同じです)。



メモリカードを開き、FSセグメントの内容を確認します。 TIB-スレッド情報ブロックを参照する必要があります。 少し見てみると、PEB-プロセス環境ブロックへのポインターが表示されています。 ntdllで適切なアドレスを選択します(PEB.FastPebLockを選択しました)。



PEBおよびTIB

コードを入力するだけで、トレース中にアドレスをntdllに変更できます。 しかし、私たちは異なった行動をします-変位を変更します。





コース中に遭遇する次の問題は、開梱者が必要な核機能のアドレスを動的に受け取ることです。 スクリーンショットは、システムの類似物である擬似関数xGetProcAddressを使用して名前のリストをループします。 MZヘッダーを解析する最初の部分は、下部にあります。





EDXは、必要な関数名のリストを指します。





注意深いと、MZヘッダーがわずかに低くなりますが、それについては後で詳しく説明します。

珍しいことではないようです。 すべては問題ありませんが、ntdllには必要なAPIがないか、少なくともプロトタイプに似ています。 しかし、少し考えてみると、kernel32.dllにあるものがあります。



  1. PVOID ExAllocatePool(POOL_TYPE PoolType, SIZE_T NumberOfBytes); VOID ExFreePool(PVOID P);



  2. PVOID ExAllocatePool(POOL_TYPE PoolType, SIZE_T NumberOfBytes); VOID ExFreePool(PVOID P);





に置き換えることができます
  1. HGLOBAL WINAPI GlobalAlloc(UINT uFlags、SIZE_T dwBytes);
  2. HGLOBAL WINAPI GlobalFree(HGLOBAL hMem);


xGetProcAddressが正常に機能するように、存在しない関数の名前を使用可能なものに変更します。 NtCloseに置き換えました。





優しくトレースして、レジスタ内のNtCloseアドレスをkernel32.dllから必要なアドレスに置き換えます。 サイクルを実行した後、次の画像に示すように、必要なすべてのアドレスが受信されます。 この問題は終わりました。続けます。





GlobalAllocに置き換えたExAllocatePoolが着実に機能していることを確認します。





彼らは静かに開梱に近づきました。





余計なものは何もないので、おそらくコードはアセンブラーで書かれたでしょう。 画面上のコードはメモリ割り当てを行い、そこに展開してから、イメージを準備し、メモリをゼロ化および消去し、成功するとOEPにジャンプします。

一見収縮したデータは、LZWバリアントと似ているように見えたため、圧縮解除アルゴリズムを研究しませんでした。





アンパッカーはAPIを使用しないため、問題なく迅速に実行されます。 実際、その後すぐに、クリーンなドライバーでリージョンをダンプできます。 しかし、私はリング3にいることで、分析をどれだけ進めることができるのだろうと思っていました。

PrepareImage関数は、展開されたイメージを準備します。必要なオフセットでセクションを再マップし、インポートからAPIアドレスを受け取り、再配置テーブルに従ってアドレスを再計算します。

私たちはIATの機能検索サイクルを定期的に行っています。これは、持っていないモジュール(ntoskrnl、halなど)を要求するだけでなく、それに応じて機能します。





ご覧のとおり、すでにサイクルに入っていますが、EIPを0x008982d7に変更し、ESPを減らしてEAX = 0に設定すると、多少は正しくなりました。 再配置を編集しても問題はありません。最終的にOEPに移動します。 ただし、インポートアドレスが復元されていないため、これを停止する必要があります。スタブを含む別のdllを作成する理由はありません。 純粋なコードは、逆アセンブラーですでに静的に解析できます。



出力の代わりに


宛先:





...以降:





一番下のスクリーンショットから検索に行を入れてあなたを苦しめないために、私はすぐにこれがRustockのバージョンの1つであると言います



繰り返しになりますが、私の怠decisionは、「額」の決定によってこれが行われるよりもずっと長く変質者になると確信しています。



All Articles