概要
スレッドの優先順位を設定するWin32 APIの関数です。
C++
__kernel_entry NTSYSCALLAPI NTSTATUS NtSetInformationThread(
[in] HANDLE ThreadHandle,
[in] THREADINFOCLASS ThreadInformationClass,
[in] PVOID ThreadInformation,
[in] ULONG ThreadInformationLength,
);
第二引数のThreadInformationClassでは、スレッドの情報のクラスを指定します。そのThreadInformationClassに文書化されていない「ThreadHideFromDebugger (0x11)」というフラグを設定することで、解析妨害手法として悪用されます。デバッガーは、上記フラグが設定されたスレッドの情報を取得することができません。このため、NtSetInfomationTread実行以降にブレークポイントを設定していた場合や関数実行以降をステップ実行した場合アプリケーションがクラッシュして終了します。
実装の例
C++/x86 Assemblyのコード例を以下に示します。NtSetInformationThreadに引数を設定して実行するコードのみを掲載しています。NtSetInformationThreadの呼び出し以降に、隠したいコードを追加することでデバッグを回避することができます。
NtSetInformationThreadのサンプルコード
C++
typedef enum THREADINFOCLASS {
ThreadHideFromDebugger = 0x11
}THREADINFOCLASS;
typedef NTSTATUS (__stdcall * pfnNtSetInformationThread) (
HANDLE ThreadHandle,
THREADINFOCLASS ThreadInformationClass,
PVOID ThreadInformation,
ULONG ThreadInformationLength
);
HMODULE hNtdll = LoadLibraryA ("ntdll.dll");
pfnNtSetInformationThread NtSetInformationThread = (pfnNtSetInformationThread) GetProcAddress(hNtdll, "NtSetInformationThread");
NtSetInformationThread(
GetCurrentThread(),
ThreadHideFromDebugger,
NULL,
0 );
Assembly(x86)
push offset LibFileName ; "ntdll.dll"
call ds:LoadLibraryA
mov [ebp+hModule], eax
push offset ProcName ; "NtSetInformationThread"
mov eax, [ebp+hModule]
push eax ; hModule
call ds:GetProcAddress
mov [ebp+var_8], eax
push 0
push 0
push 11h ; ThreadHideFromDebugger
call ds:GetCurrentThread
push eax
call [ebp+var_8] ; "NtSetInformationThread"
回避手法
NtSetInformationThreadによるデバッガーの検出を回避し、プログラムを解析する手法を以下に示します。一時的に検出を回避するときには1の手法を、恒久的に検出を回避するときには2の手法を使います.
- プラグインを利用する
ScyllaHide(Ollydbg, x64dbgなどで利用可能なプラグイン)の”NtSetInformationThread”の項目にチェックを入れることで回避できます。 - プログラムを書き換える
NtSetInformationThreadを実行するコードを削除し、変更を保存します。
検出手法
NtSetInformationThreadを検出するためのYARA ルールを以下に紹介します。
YARAルール
rule Win32API_NtSetInformationThread
{
strings:
$str = "NtSetInformationThread"
condition:
$str
}