概要
指定されたプロセスの情報を取得するWin32 APIの関数です。例えば、PEB(Process Environment Block)構造体へのポインターやWOW64環境で実行されているかどうかを判別する値など、指定する引数に応じて様々な情報を取得することができます。
C++
__kernel_entry NTSTATUS NtQueryInformationProcess(
[in] HANDLE ProcessHandle,
[in] PROCESSINFOCLASS ProcessInformationClass,
[out] PVOID ProcessInformation,
[in] ULONG ProcessInformationLength,
[out, optional] PULONG ReturnLength
);
取得する情報のタイプは第二引数のProcessInformationClassで指定します。マルウェアの解析妨害で着目されるProcessInformationClassとしては以下の3つがあります。
(1) ProcessDebugPort(0x07)
(2) ProcessDebugObjectHandle(0x1E)
(3) ProcessDebugFlags(0x1F)
上記のProcessInformationClassを指定した時、第三引数のProcessInformationの示す値が順に「-1(0xFFFFFFFF)」、「0以外」、「0」であれば、プロセスがデバッグされていることを示します。
第二引数(ProcessInformationClass)に指定する値 | 第三引数(ProcessInformation)がデバッグ時に示す値 |
---|---|
ProcessDebugPort(0x07) | -1(0xFFFFFFFF) |
ProcessDebugObjectHandle(0x1E) | 0以外 |
ProcessDebugFlags(0x1F) | 0 |
実装の例
C++/x86 Assemblyのコード例を以下に示します。
(1) ProcessDebugPort(0x07)の場合
C++
typedef NTSTATUS (NTAPI* pfnNtQueryInformationProcess)(
HANDLE ProcessHandle,
UINT ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
HMODULE hNtDll = LoadLibraryA("ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
NTSTATUS status;
DWORD dwProcessDebugPort;
status = NtQueryInformationProcess(
GetCurrentProcess(),
0x07,
&dwProcessDebugPort,
sizeof(DWORD),
NULL);
if (NT_SUCCESS(status) && dwProcessDebugPort == -1) {
exit(1);
}
Assembly
call GetCurrentProcess
push 0 ; ReturnLength
push 4 ; ProcessInformationLength
lea edx, [ebp+var_C]
push edx ; ProcessInformation
push 7 ; ProcessInformationClass
push eax ; ProcessHandle
call NtQueryInformationProcess
test eax, eax
jnz short loc_NtQueryInformationProcess_fail
cmp [ebp+var_C], 0FFFFFFFFh
jz short loc_debugger_found
loc_debugger_found:
push 1
call exit
(2) ProcessDebugObjectHandle(0x1E)の場合
C++
typedef NTSTATUS (NTAPI* pfnNtQueryInformationProcess)(
HANDLE ProcessHandle,
UINT ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
HMODULE hNtDll = LoadLibraryA("ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
NTSTATUS status;
DWORD dwProcessDebugObjectHandle;
status = NtQueryInformationProcess(
GetCurrentProcess(),
0x1e,
&dwProcessDebugObjectHandle,
sizeof(DWORD),
NULL);
if (NT_SUCCESS(status) && dwProcessDebugObjectHandle !== 0) {
exit(1);
}
Assembly
call GetCurrentProcess
push 0 ; ReturnLength
push 4 ; ProcessInformationLength
lea edx, [ebp+var_C]
push edx ; ProcessInformation
push 1Eh ; ProcessInformationClass
push eax ; ProcessHandle
call NtQueryInformationProcess
test eax, eax
jnz short loc_NtQueryInformationProcess_fail
cmp [ebp+var_C], 0
jz short loc_debugger_found
loc_debugger_found:
push 1
call exit
(3) ProcessDebugFlags(0x1F)の場合
C++
typedef NTSTATUS (NTAPI* pfnNtQueryInformationProcess)(
HANDLE ProcessHandle,
UINT ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
HMODULE hNtDll = LoadLibraryA("ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
NTSTATUS status;
DWORD dwProcessDebugFlags;
status = NtQueryInformationProcess(
GetCurrentProcess(),
0x1f,
&dwProcessDebugFlags,
sizeof(DWORD),
NULL);
if (NT_SUCCESS(status) && dwdwProcessDebugFlags == 0) {
exit(1);
}
Assembly
call GetCurrentProcess
push 0 ; ReturnLength
push 4 ; ProcessInformationLength
lea edx, [ebp+var_C]
push edx ; ProcessInformation
push 1Fh ; ProcessInformationClass
push eax ; ProcessHandle
call NtQueryInformationProcess
or eax,[ebp+var_C]
jz short loc_debugger_found
loc_debugger_found:
push 1
call exit
回避手法
NtQueryInformationProcessによるデバッガー検出を回避し、プログラムを解析する手法を以下に示します。
- ProcessInformationの値を変更する
NtQueryInformationProcess関数を呼び出した後に、指定したProcessInformationClassに応じてProcessInformationが示す値を以下のように変更します。
・ProcessDebugPort(0x07)の場合:-1(0xFFFFFFFF)以外に変更
・ProcessDebugObjectHandle(0x1E)の場合:0に変更
・ProcessDebugFlags(0x1F)の場合:0以外に変更 - プラグインを利用する
ScyllaHide(Ollydbg, x64dbgなどで利用可能なプラグイン)の”NtQueryInformationProcess”の項目にチェックを入れます。 - プログラムを書き換える
NtQueryInformationProcessを呼び出した後の分岐命令をJMP命令またはNOP命令に書き換え、変更を保存します。
検出手法
NtQueryInformationProcessを検出するYARAルールを以下に示します。
YARAルール
rule Win32API_NtQueryInformationProcess
{
strings:
$str = "NtQueryInformationProcess"
condition:
$str
}