いろいろなプロセス・インジェクションの手法

プロセス・インジェクションの手法についてまとめてみました。
といっても、こちらの記事の内容をざっくり翻訳しただけですが。

※この記事を最初に投稿したのは2018年11月ですが、内容的に誤っている部分や不正確な部分は適宜修正しています。

はじめに

プロセスとは:
メモリ上で実行中のプログラムのこと。

スレッドとは:
プロセス内で命令を逐次実行する部分のこと。プログラム中の関数の実行を担う。

1. CreateRemoteThread関数およびLoadLibrary関数を用いたDLLインジェクション

多分、最も一般的(?)な方法。正規のプロセスに悪意のあるDLLを読み込ませて悪いことをします。
大まかな仕組み:
標的プロセス上にLoadLibrary関数経由で悪意のあるDLLを読み込ませるスレッドを作成することで実現します。

手順としては:
1. CreateToolhelp32Snapshot、Process32First、Process32Next等を用いて標的となるプロセスを探し出し、OpenProcess関数を用いて標的プロセスのハンドルを取得します。
2. VirtualAllocEx関数で標的プロセス上のメモリ領域を確保します。
3. 2.で確保したメモリ領域にWriteProcessMemory関数で悪意のあるDLLへのパス名を標的プロセス上に書き込みます。
4. GetProcAddress関数でLoadLibrary関数のアドレスを取得します。
5. 標的プロセスのハンドル、LoadLibrary関数のアドレス、標的プロセス上に書き込まれた悪意のあるDLLへのパス名へのポインタを引数に渡して、CreateRemoteThread関数を呼び出します。これにより、標的プロセスが悪意のあるDLLをロードします。(もう少し厳密に言うと標的プロセス上に悪意のあるDLLをLoadLibrary関数経由でロードするスレッドが作成されます。)

CreateRemoteThread関数の詳細はこちら
※ CreateRemoteThread関数のかわりにNtCreateThreadEx関数やRtlCreateUserThread関数が使われる場合もあります。

DLLがロードされるとOSはDLL中のDllMain関数を自動的に実行します。そのため攻撃者は新しく関数を定義しなくてもDllMain関数に悪意のあるコードを埋め込むことで目的を達成できます。

解析のチートシート
・標的プロセス名を特定したい場合は、strncmpやmemcmpなどの値の比較を行う関数を確認してみること。これらの関数に標的プロセス名が値として渡されている事が多い。
・悪意のあるDLLの名前を特定したい場合は、WriteProcessMemory関数の呼び出し部分にブレークポイントをセットし、スタックの内容をダンプすること。

  • DLLインジェクションを取り扱ったCTFのWriteUP
  • Practical Malware AnalysisのLab 12-1 はDLLインジェクションをテーマにしている

2. Portable Executable (PE) インジェクション

LoadLibrary関数を実行し悪意のあるDLLを読み込ませるのではなく、標的プロセスに悪意のあるコードをコピーしてシェルコードやCreateRemoteThread関数を介して実行する手法です。1.の手法との大きな違いは悪意のあるDLLをディスク上に作成する必要がないという点です。
手順としては:

1. VirtualAllocEx関数で標的プロセス上のメモリ領域を確保します。
2. 1.で確保したメモリ領域にWriteProcessMemory関数で悪意のあるコードをコピーします。
3. CreateRemoteThread関数 (あるいはNtCreateThreadEx関数やRtlCreateUserThread関数)を用いて標的プロセス上で悪意のあるコードを実行します。

この手法の留意点として、他のプロセスにPEをコピーするとベース・アドレスが新しいものに変わります。そのため攻撃者は不正コードを実行する前に標的プロセス上のrelocation tableのアドレスを探し出し、relocation tableをもとにコピーされた不正コードの絶対アドレスを取得する必要があります。

3. Process Hollowing / Replacement (プロセス・ホロウイング / リプレイスメント)

正規のプロセスのメモリ領域をアンマップして空いたメモリ領域に悪意のあるコードを書き込む手法です。正規のプロセスをまるごと悪意のあるプロセスに上書きするイメージです。
手順としては:

1. まず正規のプロセスをサスペンド・モードで立ち上げます。CreateProcess関数をCREATE_SUSPENDEDフラグ付きで呼び出す(dwCreationFlagsを0x4に設定)ことで実現します。サスペンド・モードで立ち上げられたプロセスはResumeThread関数が呼び出されるまで実行されません。
2. 1.で立ち上げた正規プロセスのメモリ領域をZwUnmapViewOfSection関数やNtUnmapViewOfSection関数を用いてアンマップ(空っぽ)にします。
3. 2.で空いたメモリ領域上にVirtualAllocEx関数を用いて不正コードを書き込むためのメモリ領域を確保し、WriteProcessMemoryで不正コードを書き込みます。
4. ResumeThread関数でプロセスをサスペンド・モードから実行状態に移します。

書き換えられたプロセスは書き換え前と同じ権限レベルで実行されます。

  • プロセス・ホロウイングを取り扱ったCTFのWriteUp
  • Practical Malware AnalysisのLab 12-2 はプロセス・ホロウイングをテーマにしている

4. スレッド実行ハイジャック (またはSUSPEND, INJECT AND RESUME (SIR))

先述したProcess Hollowingと似た挙動をします。スレッド実行ハイジャックでは、マルウェアは既存のプロセスのスレッドを標的にします。
手順としては:

1. CreateToolhelp32Snapshot関数とThread32First関数を呼び出したあとにOpenThread関数を呼び出し、標的スレッドのハンドルを取得します。
2. SuspendThread関数を呼び出して標的スレッドをサスペンド・モードにします。
3. VirtualAllocEx関数でメモリ領域を確保し、WriteProcessMemory関数で悪意のあるコードを書き込みます。
4. 3.で書き込まれる不正コードはシェルコード、悪意のあるDLLへのパス情報、LoadLibrary関数のアドレス情報などです。
5. 標的スレッドをサスペンド・モードから実行状態に移します。

5. SetWindowsHookEx関数を用いたフック・インジェクション

フックとは関数が呼び出された際に割り込みをかける手法です。マルウェアはフックを利用して、特定のスレッドで特定のイベントが起きたときに悪意のあるDLLをロードさせることができます。
この手法では主にSetWindowsHookEx関数が用いられます。SetWindowsHookEx関数は4つの引数を受け取ります。

  • 1つ目の引数:イベント・タイプ。キーボードの押下(WH_KEYBOARD)、マウスの動作(WH_MOUSE)など様々なタイプのイベントがあります。
  • 2つ目の引数:特定のイベントが発生した際に実行させる悪意のある関数へのポインタ。
  • 3つ目の引数:悪意のある関数を含んでいるモジュールまたはDLLへのハンドル。
    ※対象のモジュールやDLLへのハンドル、関数のアドレスを特定するため、SetWindowsHookEx関数を呼び出す前にLoadLibrary関数(モジュールやDLLのハンドルを取得する)およびGetProcAddress関数(関数のアドレスを取得する)への呼び出しが行われることが多々あります。
  • 4つ目の引数:フックしたいスレッド。もし、この値が0に設定されていた場合は目的のイベントが発生した際にすべてのスレッドでアクションが起きます。ただし通常マルウェアは1つのスレッドを標的とします。そのため標的スレッドを特定するため、SetWindowsHookEx関数を呼び出す前にCreateToolhelp32Snapshot関数とThread32Next関数を呼び出すことが多いです。

6. レジストリを介したインジェクションおよび永続化 (APPINIT_DLLS, APPCERTDLLS, IFEOなど)

Appinit_DLL、 AppCertDlls、IFEO (Image File Execution Options)はマルウェアがインジェクションや永続化を図るためによく利用するレジストリ・キーです。
以下に配置されています:

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls
HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls
HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls
HKLM\Software\Microsoft\Windows NT\currentversion\image file execution options

Appinit_DLL

マルウェアは悪意のあるライブラリをAppinit_DLLレジストリ配下に配置することにより、他のプロセスに不正ライブラリをロードさせることができます。User32.dllをロードするプロセスはAppinit_DLLレジストリ配下にあるライブラリも読み込みます。User32.dllはダイアログボックスなどのグラフィカルな処理をする際に頻繁に使用されるライブラリです。そのためマルウェアによってAppinit_DLL レジストリが編集された場合、大半のプロセスが悪意のあるライブラリをロードすることになります。
Practical Malware AnalysisのLab 11-2はこのAppinit_DLLをテーマにしている。

AppCertDlls

AppInit_DLLsを悪用した手法と類似した手法です。AppCertDllsレジストリ配下のDLLはプロセスが以下の関数を呼び出した際にロードされます:

  • CreateProcess
  • CreateProcessAsUser
  • CreateProcessWithLogonW
  • CreateProcessWithTokenW
  • WinExec

Image File Execution Options (IFEO)

IFEOは主にデバッグ目的で使用されます。開発者はIFEOレジストリ・キーの"Debugger"の値を編集して実行ファイルに別のプログラムをアタッチすることによってデバッグを行うことができます。デバッグ対象の実行ファイルが起動すると、その実行ファイルにアタッチされたプログラム(通常であればデバッガ)が実行されます。
やり方はシンプルで標的となる実行ファイルにデバッガのパスを渡すだけです。ただしWindowsは渡されたプログラムが本当にデバッガなのかという確認まではしません。ただ単純に"Debugger Value"に設定されているプログラムを渡すだけです。なので例えば HKLM\Software\Microsoft\Windows NT\currentversion\image file execution options\calc.exeの"Debugger"の値をnotepad.exeに設定すると電卓プログラムを起動しようとするたびにメモ帳が起動されることになります。
マルウェアはIFEOレジストリ・キーの"Debugger"の値を編集することにより、任意のプログラムの起動時に悪意のあるプログラムを実行させることができます。

こちらの記事でIFEOレジストリを編集して、Windowsのログオン画面のスクリーンショットを取る方法が紹介されています。

7. APCインジェクション

Asynchronous Procedure Calls (APC)を利用する手法です。APCとは特定のスレッドで非同期的に実行される関数のことです。各スレッドにはAPCのキューがあり、スレッドが変更可能状態に入るとキューされたAPC関数がスレッドによって実行されます。詳しくはこちら
マルウェアは任意のスレッドのAPCキューにコードをアタッチすることによって、そのスレッドにコードを実行させることができます。マルウェアは変更可能状態にあるスレッドを見つけるとOpenThread関数およびQueueUserAPC関数を呼び出してAPC関数をスレッドにキューさせます。スレッドが変更可能状態に入るとキューされたAPC関数がスレッドによって実行されます。

QueueUserAPC関数を利用して標的プロセス(のスレッド)に悪意のあるDLLを読み込ませる

大まかな手順はCreateRemoteThread関数を利用したDLLインジェクションと同じです。標的プロセスのハンドルを取得し、標的プロセス上にメモリ領域を確保し、確保したメモリ領域に悪意のあるDLLのパス名を書き込み、LoadLibrary関数のアドレスを取得します。ただし、最後にCreateRemoteThread関数ではなくQueueUserAPC関数を利用して標的プロセスに悪意のあるDLLを読み込ませます。

QueueUserAPC関数は以下の3つの引数を受け取ります:

1つ目の引数:  実行したいAPC関数へのポインタ (例: LoadLibrary関数へのポインタ)
2つ目の引数:標的プロセスのスレッドのハンドル
3つ目の引数:1つ目の引数で指定したAPC関数のポインタに渡すデータ (例: LoadLibrary関数に渡す悪意のあるDLLのパス名)

以上でインジェクションの準備が完了しました。あとは標的プロセスのスレッドが変更可能状態に入れば、スレッドがAPCキューからAPC関数を実行します。上記の例ではAPC関数としてLoadLibrary関数を悪意のあるDLLのパス名とともに指定しているので、標的プロセスのスレッドが変更可能状態に入るとスレッドがLoadLibrary関数を実行し、標的プロセスに悪意のあるDLLが読み込まれます。

※APCインジェクションを用いたUrsnifの感染例が報告されています。この例では悪意のあるDLLのパス名ではなく、悪意のあるDLLのペイロードそのものを標的プロセスに書き込んでいます。QueueUserAPC関数の1つ目の引数には悪意のあるDLLのアドレスが渡されます。

8. SETWINDOWLONG関数を用いた拡張ウィンドウ・メモリへのインジェクション

エクスプローラーの拡張ウィンドウ・メモリにインジェクトする手法です。ウィンドウ・クラスを使用する際、アプリケーションは追加のメモリのバイト数を指定することができます。これを拡張ウィンドウ・メモリ (Extra Window Memory (EWM))と呼びます。しかし拡張ウィンドウ・メモリはそれほど大きくはありません。そこでマルウェアはexplorer.exeの共有セクションにコードを書き込み、SetWindowLong関数とSendNotifyMessage関数を使用してシェルコードへのポインタを作成し、シェルコードを実行します。

マルウェアが共有セクションへ書き込むには2通り方法があります。1つは共有セクションを作成しマルウェア自身と他のプロセス(例:explorer.exe)をそこにマッピングする方法で、もう1つはすでに存在している共有セクションに書き込む方法です。前者よりも後者のほうが手順としては簡易なため、後者の方法が用いられることが多いです。
マルウェアは共有セクションに悪意のあるコードを書き込んだ後、GetWindowLong関数とSetWindowLong関数を使用して"Shell_TrayWnd"の拡張ウィンドウ・メモリにアクセス・変更します。GetWindowLong関数は拡張ウィンドウ・メモリ内の指定されたオフセットにあるデータ (32ビット値)を取得するための関数で、SetWindowLong関数は指定されたオフセットの値 (32ビット値)を変更するための関数です。これらの関数を使用してマルウェアはウィンドウ・クラス内の関数ポインタのオフセットを変更し、共有セクションに書き込まれたシェルコードにポイントさせることができます。

これまで紹介した手法では不正コードを実行するためにCreateRemoteThread関数、QueueUserAPC関数、SetThreadContext関数などを利用していましたが、拡張ウィンドウ・メモリ・インジェクションでは不正コードの実行にSendNotifyMessage関数を利用します。SendNotifyMessage関数が実行されるとShell_TrayWndはSetWindowLong関数によって設定されたアドレス (この場合はシェルコードへのアドレス)に制御を渡します。

9. SHIMを用いたインジェクション

ShimとはMicrosoftが提供している互換性に関する問題を解決するための仕組みです。Shimを利用することによって開発者はコードを書き直すことなくプログラムを修正することができます。Shimは基本的に対象となるプログラムのAPIコールをフックすることによって様々な制御を行います。
※Shimについては、こちらの記事でも取り上げています。

マルウェアはShimを悪用することによりプログラムへのインジェクションやマルウェア自身の永続化を達成することができます。もっとも代表的なShimの悪用方法はsdbinst.exeを実行して悪意のあるsdbファイルを読み込ませる方法です。
例として、あるアドウェアは"InjectDLL" shim(プロセスに特定のDLLを読み込ませるためのShim)を利用してGoogle Chromeブラウザ・アプリケーションに悪意のあるDLLを読み込ませます。

10. IATフックおよびインライン・フック

IATフックはマルウェアがIAT (Import Address Table)を書き換える手法のことです。IATフックによってIATの内容が書き換えられると、正規のプログラムがDLL内のAPIを呼び出した際に、正規の関数ではなく、書き換えられた関数が実行されます。
インライン・フックでは、マルウェアはAPI関数そのものを編集します。具体的にはフックしたい関数の先頭数バイトに悪意のある関数へのジャンプ命令を上書きします。

プロセス・インジェクションの主な目的は正規のプロセスに悪意のあるコードを注入することによって、検知を回避する、あるいは困難にすることです。
大抵の場合、攻撃者はインジェクションを行うためのファイルやスクリプト(インジェクター)を用意する必要があります。さらにインジェクションを継続的に行いたい場合はインジェクターを永続化する必要がありますが、多くの場合、Runレジストリキーやスケジュールタスク、Windowsサービスなど通常のマルウェアが使用するような永続化のメカニズムを用いるので、仮にプロセス・インジェクションそのものの検知が困難であったとしてもインジェクターを特定することによって不正活動を検知することができます。

以上。

参考
Learning Malware Analysis (By Monnappa K A)  P.297 - 330

Leave a Reply

Your email address will not be published. Required fields are marked *