NtQueryInformationProcess

この記事をシェア

概要

指定されたプロセスの情報を取得する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
}
この記事をシェア
サイバーセキュリティ
情報局の最新情報を
チェック!