Tutorial NtQuerySystemInformation and Process IDs

D

Deleted member 65228

Guest
#1
Hello.

One of the most common methods for enumerating through the running processes for finding the Process ID of a process from the executable name is via Win32 API functions like CreateToolhelp32Snapshot and then walking the snapshot list with a do loop. I decided I'd share an alternate approach which relies on the NTAPI function NtQuerySystemInformation, which is what is internally called by the OS when those Win32 API approaches are used.

There is no need to use NtQuerySystemInformation specifically for enumerating the processes to retrieve the Process ID, but there will be many other uses of this function. When the traditional Win32 API method via CreateToolhelp32Snapshot is called, a function called TH32CreateSnapshot will be called (this is the function that internally calls NtQuerySystemInformation). Many other functions are called of course for the functionality though.

The function prototype of NtQuerySystemInformation.
Code:
typedef NTSTATUS(NTAPI *pNtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength OPTIONAL
    );
Example demonstration code.
stdafx.h

Code:
#pragma once
#include <Windows.h>
#include <cstdio>

#include "winternl.h"
#include "ntapi.h"
winternl.h (slightly altered version of the Windows copy)
Code:
/************************************************************************
*                                                                       *
*   winternl.h -- This module defines the internal NT APIs and data     *
*       structures that are intended for the use only by internal core  *
*       Windows components.  These APIs and data structures may change  *
*       at any time.                                                    *
*                                                                       *
*   These APIs and data structures are subject to changes from one      *
*       Windows release to another Windows release.  To maintain the    *
*       compatiblity of your application, avoid using these APIs and    *
*       data structures.                                                *
*                                                                       *
*   The appropriate mechanism for accessing the functions defined in    *
*       this header is to use LoadLibrary() for ntdll.dll and           *
*       GetProcAddress() for the particular function.  By using this    *
*       approach, your application will be more resilient to changes    *
*       for these functions between Windows releases.  If a function    *
*       prototype does change, then GetProcAddress() for that function  *
*       might detect the change and fail the function call, which your  *
*       application will be able to detect.  GetProcAddress() may not   *
*       be able to detect all signature changes, thus avoid using these *
*       internal functions.  Instead, your application should use the   *
*       appropriate Win32 function that provides equivalent or similiar *
*       functionality.                                                  *
*                                                                       *
*   Copyright (c) Microsoft Corp. All rights reserved.                  *
*                                                                       *
************************************************************************/

#ifndef _WINTERNL_
#define _WINTERNL_
#include <winapifamily.h>

#pragma region Desktop Family or OneCore Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)


#if (_WIN32_WINNT >= 0x0500)

#include <windef.h>

#ifdef __cplusplus
extern "C" {
#endif

    //
    // These data structures and type definitions are needed for compilation and
    // use of the internal Windows APIs defined in this header.
    //

    typedef _Return_type_success_(return >= 0) LONG NTSTATUS;

    typedef CONST char *PCSZ;

    typedef struct _STRING {
        USHORT Length;
        USHORT MaximumLength;
        PCHAR Buffer;
    } STRING;
    typedef STRING *PSTRING;

    typedef STRING ANSI_STRING;
    typedef PSTRING PANSI_STRING;
    typedef PSTRING PCANSI_STRING;

    typedef STRING OEM_STRING;
    typedef PSTRING POEM_STRING;
    typedef CONST STRING* PCOEM_STRING;

    typedef struct _UNICODE_STRING {
        USHORT Length;
        USHORT MaximumLength;
        PWSTR  Buffer;
    } UNICODE_STRING;
    typedef UNICODE_STRING *PUNICODE_STRING;
    typedef const UNICODE_STRING *PCUNICODE_STRING;

    //
    // The PEB_LDR_DATA, LDR_DATA_TABLE_ENTRY, RTL_USER_PROCESS_PARAMETERS, PEB
    // and TEB structures are subject to changes between Windows releases; thus,
    // the field offsets and reserved fields may change. The reserved fields are
    // reserved for use only by the Windows operating systems. Do not assume a
    // maximum size for these structures.
    //
    // Instead of using the InMemoryOrderModuleList field of the
    //     LDR_DATA_TABLE_ENTRY structure, use the Win32 API EnumProcessModules
    //
    // Instead of using the IsBeingDebugged field of the PEB structure, use the
    //     Win32 APIs IsDebuggerPresent or CheckRemoteDebuggerPresent
    //
    // Instead of using the SessionId field of the PEB structure, use the Win32
    //     APIs GetCurrentProcessId and ProcessIdToSessionId
    //
    // Instead of using the Tls fields of the TEB structure, use the Win32 APIs
    //     TlsAlloc, TlsGetValue, TlsSetValue and TlsFree
    //
    // Instead of using the ReservedForOle field, use the COM API
    //     CoGetContextToken
    //
    // Sample x86 assembly code that gets the SessionId (subject to change
    //     between Windows releases, use the Win32 APIs to make your application
    //     resilient to changes)
    //     mov     eax,fs:[00000018]
    //     mov     eax,[eax+0x30]
    //     mov     eax,[eax+0x1d4]
    //

    //
    // N.B. Fields marked as reserved do not necessarily reflect the structure
    //      of the real struct. They may simply guarantee that the offets of
    //      the exposed fields are correct. When code matches this pattern,
    //
    //          TYPE1 ExposedField1;
    //          BYTE ReservedBytes[b];
    //          PVOID ReservedPtrs[p];
    //          TYPE2 ExposedField2;
    //
    //      or that pattern with ReservedBytes and ReservedPtrs swapped, it is
    //      likely that 'b' and 'p' are derived from the following system:
    //
    //          GapThirtyTwo = 4p + b
    //          GapSixtyFour = 8p + b
    //
    //      where GapThirtyTwo is the number of bytes between the two exposed
    //      fields in the 32-bit version of the real struct and GapSixtyFour
    //      is the number of bytes between the two exposed fields in the 64-bit
    //      version of the real struct.
    //
    //      Also note that such code must take into account the alignment of
    //      the ReservedPtrs field.
    //

    typedef struct _PEB_LDR_DATA {
        BYTE Reserved1[8];
        PVOID Reserved2[3];
        LIST_ENTRY InMemoryOrderModuleList;
    } PEB_LDR_DATA, *PPEB_LDR_DATA;

    typedef struct _LDR_DATA_TABLE_ENTRY {
        PVOID Reserved1[2];
        LIST_ENTRY InMemoryOrderLinks;
        PVOID Reserved2[2];
        PVOID DllBase;
        PVOID Reserved3[2];
        UNICODE_STRING FullDllName;
        BYTE Reserved4[8];
        PVOID Reserved5[3];
#pragma warning(push)
#pragma warning(disable: 4201) // we'll always use the Microsoft compiler
        union {
            ULONG CheckSum;
            PVOID Reserved6;
        } DUMMYUNIONNAME;
#pragma warning(pop)
        ULONG TimeDateStamp;
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

    typedef struct _RTL_USER_PROCESS_PARAMETERS {
        BYTE Reserved1[16];
        PVOID Reserved2[10];
        UNICODE_STRING ImagePathName;
        UNICODE_STRING CommandLine;
    } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

    typedef
        VOID
        (NTAPI *PPS_POST_PROCESS_INIT_ROUTINE) (
            VOID
            );

    typedef struct _PEB {
        BYTE Reserved1[2];
        BYTE BeingDebugged;
        BYTE Reserved2[1];
        PVOID Reserved3[2];
        PPEB_LDR_DATA Ldr;
        PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
        PVOID Reserved4[3];
        PVOID AtlThunkSListPtr;
        PVOID Reserved5;
        ULONG Reserved6;
        PVOID Reserved7;
        ULONG Reserved8;
        ULONG AtlThunkSListPtr32;
        PVOID Reserved9[45];
        BYTE Reserved10[96];
        PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
        BYTE Reserved11[128];
        PVOID Reserved12[1];
        ULONG SessionId;
    } PEB, *PPEB;

    typedef struct _TEB {
        PVOID Reserved1[12];
        PPEB ProcessEnvironmentBlock;
        PVOID Reserved2[399];
        BYTE Reserved3[1952];
        PVOID TlsSlots[64];
        BYTE Reserved4[8];
        PVOID Reserved5[26];
        PVOID ReservedForOle;  // Windows 2000 only
        PVOID Reserved6[4];
        PVOID TlsExpansionSlots;
    } TEB, *PTEB;

    typedef struct _OBJECT_ATTRIBUTES {
        ULONG Length;
        HANDLE RootDirectory;
        PUNICODE_STRING ObjectName;
        ULONG Attributes;
        PVOID SecurityDescriptor;
        PVOID SecurityQualityOfService;
    } OBJECT_ATTRIBUTES;
    typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;

    typedef struct _IO_STATUS_BLOCK {
#pragma warning(push)
#pragma warning(disable: 4201) // we'll always use the Microsoft compiler
        union {
            NTSTATUS Status;
            PVOID Pointer;
        } DUMMYUNIONNAME;
#pragma warning(pop)

        ULONG_PTR Information;
    } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

    typedef
        VOID
        (NTAPI *PIO_APC_ROUTINE) (
            IN PVOID ApcContext,
            IN PIO_STATUS_BLOCK IoStatusBlock,
            IN ULONG Reserved
            );

    typedef struct _PROCESS_BASIC_INFORMATION {
        PVOID Reserved1;
        PPEB PebBaseAddress;
        PVOID Reserved2[2];
        ULONG_PTR UniqueProcessId;
        PVOID Reserved3;
    } PROCESS_BASIC_INFORMATION;
    typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;

    typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
        LARGE_INTEGER IdleTime;
        LARGE_INTEGER KernelTime;
        LARGE_INTEGER UserTime;
        LARGE_INTEGER Reserved1[2];
        ULONG Reserved2;
    } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;

    typedef struct _SYSTEM_PROCESS_INFORMATION {
        ULONG NextEntryOffset;
        ULONG NumberOfThreads;
        LARGE_INTEGER Reserved[3];
        LARGE_INTEGER CreationTime;
        LARGE_INTEGER UserTime;
        LARGE_INTEGER KernelTime;
        UNICODE_STRING ImageFileName;
        ULONG BasePriority;
        HANDLE UniqueProcessId;
        HANDLE InheritedFromUniqueProcessId;
        //SYSTEM_THREAD SystemThreads[];
    } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

    typedef struct _SYSTEM_REGISTRY_QUOTA_INFORMATION {
        ULONG RegistryQuotaAllowed;
        ULONG RegistryQuotaUsed;
        PVOID Reserved1;
    } SYSTEM_REGISTRY_QUOTA_INFORMATION, *PSYSTEM_REGISTRY_QUOTA_INFORMATION;

    typedef struct _SYSTEM_BASIC_INFORMATION {
        BYTE Reserved1[24];
        PVOID Reserved2[4];
        CCHAR NumberOfProcessors;
    } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;

    typedef struct _SYSTEM_TIMEOFDAY_INFORMATION {
        BYTE Reserved1[48];
    } SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION;

    typedef struct _SYSTEM_PERFORMANCE_INFORMATION {
        BYTE Reserved1[312];
    } SYSTEM_PERFORMANCE_INFORMATION, *PSYSTEM_PERFORMANCE_INFORMATION;

    typedef struct _SYSTEM_EXCEPTION_INFORMATION {
        BYTE Reserved1[16];
    } SYSTEM_EXCEPTION_INFORMATION, *PSYSTEM_EXCEPTION_INFORMATION;

    typedef struct _SYSTEM_LOOKASIDE_INFORMATION {
        BYTE Reserved1[32];
    } SYSTEM_LOOKASIDE_INFORMATION, *PSYSTEM_LOOKASIDE_INFORMATION;

    typedef struct _SYSTEM_INTERRUPT_INFORMATION {
        BYTE Reserved1[24];
    } SYSTEM_INTERRUPT_INFORMATION, *PSYSTEM_INTERRUPT_INFORMATION;

    typedef struct _SYSTEM_POLICY_INFORMATION {
        PVOID Reserved1[2];
        ULONG Reserved2[3];
    } SYSTEM_POLICY_INFORMATION, *PSYSTEM_POLICY_INFORMATION;

    typedef enum _FILE_INFORMATION_CLASS {
        FileDirectoryInformation = 1
    } FILE_INFORMATION_CLASS;

    typedef enum _PROCESSINFOCLASS {
        ProcessBasicInformation = 0,
        ProcessDebugPort = 7,
        ProcessWow64Information = 26,
        ProcessImageFileName = 27,
        ProcessBreakOnTermination = 29
    } PROCESSINFOCLASS;

    typedef enum _SYSTEM_INFORMATION_CLASS {
        SystemBasicInformation = 0,
        SystemPerformanceInformation = 2,
        SystemTimeOfDayInformation = 3,
        SystemProcessInformation = 5,
        SystemProcessorPerformanceInformation = 8,
        SystemInterruptInformation = 23,
        SystemExceptionInformation = 33,
        SystemRegistryQuotaInformation = 37,
        SystemLookasideInformation = 45,
        SystemPolicyInformation = 134,
    } SYSTEM_INFORMATION_CLASS;

    //
    // Object Information Classes
    //

    typedef enum _OBJECT_INFORMATION_CLASS {
        ObjectBasicInformation = 0,
        ObjectTypeInformation = 2
    } OBJECT_INFORMATION_CLASS;

    //
    //  Public Object Information definitions
    //

    typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION {
        ULONG Attributes;
        ACCESS_MASK GrantedAccess;
        ULONG HandleCount;
        ULONG PointerCount;

        ULONG Reserved[10];    // reserved for internal use

    } PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION;

    typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {

        UNICODE_STRING TypeName;

        ULONG Reserved[22];    // reserved for internal use

    } PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;

#if (_WIN32_WINNT >= 0x0501)
    //
    // use the WTS API instead
    //     WTSGetActiveConsoleSessionId
    // The active console id is cached as a volatile ULONG in a constant
    // memory location.  This x86 memory location is subject to changes between
    // Windows releases.  Use the WTS API to make your application resilient to
    // changes.
    //
#define INTERNAL_TS_ACTIVE_CONSOLE_ID ( *((volatile ULONG*)(0x7ffe02d8)) )
#endif // (_WIN32_WINNT >= 0x0501)

    //
    // These functions are intended for use by internal core Windows components
    // since these functions may change between Windows releases.
    //

#define RtlMoveMemory(Destination,Source,Length) memmove((Destination),(Source),(Length))
#define RtlFillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length))
#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))

#define LOGONID_CURRENT     ((ULONG)-1)
#define SERVERNAME_CURRENT  ((HANDLE)NULL)

    typedef enum _WINSTATIONINFOCLASS {
        WinStationInformation = 8
    } WINSTATIONINFOCLASS;


    typedef struct _WINSTATIONINFORMATIONW {
        BYTE Reserved2[70];
        ULONG LogonId;
        BYTE Reserved3[1140];
    } WINSTATIONINFORMATIONW, *PWINSTATIONINFORMATIONW;

    //
    // this function is implemented in winsta.dll (you need to loadlibrary to call this function)
    // this internal function retrives the LogonId (also called SessionId) for the current process
    // You should avoid using this function as it can change. you can retrieve the same information
    // Using public api WTSQuerySessionInformation. Pass WTSSessionId as the WTSInfoClass parameter
    //
    typedef BOOLEAN(WINAPI * PWINSTATIONQUERYINFORMATIONW)(
        HANDLE, ULONG, WINSTATIONINFOCLASS, PVOID, ULONG, PULONG);

    //
    // Generic test for success on any status value (non-negative numbers
    // indicate success).
    //

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

    //
    // Generic test for information on any status value.
    //

#ifndef NT_INFORMATION
#define NT_INFORMATION(Status) ((((ULONG)(Status)) >> 30) == 1)
#endif

    //
    // Generic test for warning on any status value.
    //

#ifndef NT_WARNING
#define NT_WARNING(Status) ((((ULONG)(Status)) >> 30) == 2)
#endif

    //
    // Generic test for error on any status value.
    //

#ifndef NT_ERROR
#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)
#endif

    //++
    //
    // VOID
    // InitializeObjectAttributes(
    //     OUT POBJECT_ATTRIBUTES p,
    //     IN PUNICODE_STRING n,
    //     IN ULONG a,
    //     IN HANDLE r,
    //     IN PSECURITY_DESCRIPTOR s
    //     )
    //
    //--

#ifndef InitializeObjectAttributes
#define InitializeObjectAttributes( p, n, a, r, s ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );          \
    (p)->RootDirectory = r;                             \
    (p)->Attributes = a;                                \
    (p)->ObjectName = n;                                \
    (p)->SecurityDescriptor = s;                        \
    (p)->SecurityQualityOfService = NULL;               \
    }
#endif

    //
    // Valid values for the Attributes field
    //

#define OBJ_INHERIT                         0x00000002L
#define OBJ_PERMANENT                       0x00000010L
#define OBJ_EXCLUSIVE                       0x00000020L
#define OBJ_CASE_INSENSITIVE                0x00000040L
#define OBJ_OPENIF                          0x00000080L
#define OBJ_OPENLINK                        0x00000100L
#define OBJ_KERNEL_HANDLE                   0x00000200L
#define OBJ_FORCE_ACCESS_CHECK              0x00000400L
#define OBJ_IGNORE_IMPERSONATED_DEVICEMAP   0x00000800L
#define OBJ_DONT_REPARSE                    0x00001000L
#define OBJ_VALID_ATTRIBUTES                0x00001FF2L

    //
    // Define the create disposition values
    //

#define FILE_SUPERSEDE                  0x00000000
#define FILE_OPEN                       0x00000001
#define FILE_CREATE                     0x00000002
#define FILE_OPEN_IF                    0x00000003
#define FILE_OVERWRITE                  0x00000004
#define FILE_OVERWRITE_IF               0x00000005
#define FILE_MAXIMUM_DISPOSITION        0x00000005

    //
    // Define the create/open option flags
    //

#define FILE_DIRECTORY_FILE                     0x00000001
#define FILE_WRITE_THROUGH                      0x00000002
#define FILE_SEQUENTIAL_ONLY                    0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING          0x00000008

#define FILE_SYNCHRONOUS_IO_ALERT               0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT            0x00000020
#define FILE_NON_DIRECTORY_FILE                 0x00000040
#define FILE_CREATE_TREE_CONNECTION             0x00000080

#define FILE_COMPLETE_IF_OPLOCKED               0x00000100
#define FILE_NO_EA_KNOWLEDGE                    0x00000200
#define FILE_OPEN_REMOTE_INSTANCE               0x00000400
#define FILE_RANDOM_ACCESS                      0x00000800

#define FILE_DELETE_ON_CLOSE                    0x00001000
#define FILE_OPEN_BY_FILE_ID                    0x00002000
#define FILE_OPEN_FOR_BACKUP_INTENT             0x00004000
#define FILE_NO_COMPRESSION                     0x00008000

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
#define FILE_OPEN_REQUIRING_OPLOCK              0x00010000
#endif

#define FILE_RESERVE_OPFILTER                   0x00100000
#define FILE_OPEN_REPARSE_POINT                 0x00200000
#define FILE_OPEN_NO_RECALL                     0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY          0x00800000

#define FILE_VALID_OPTION_FLAGS                 0x00ffffff
#define FILE_VALID_PIPE_OPTION_FLAGS            0x00000032
#define FILE_VALID_MAILSLOT_OPTION_FLAGS        0x00000032
#define FILE_VALID_SET_FLAGS                    0x00000036

    //
    // Define the I/O status information return values for NtCreateFile/NtOpenFile
    //

#define FILE_SUPERSEDED                 0x00000000
#define FILE_OPENED                     0x00000001
#define FILE_CREATED                    0x00000002
#define FILE_OVERWRITTEN                0x00000003
#define FILE_EXISTS                     0x00000004
#define FILE_DOES_NOT_EXIST             0x00000005

#ifdef __cplusplus
}
#endif

#endif // (_WIN32_WINNT >= 0x0500)


#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) */
#pragma endregion

#endif // _WINTERNL_
ntapi.h
Code:
#pragma once
#include "stdafx.h"

#define STATUS_SUCCESS 0x00000000
#define STATUS_UNSUCCESSFUL 0xC0000001

#define NtCurrentProcess() ((HANDLE)(LONG_PTR) -1)

typedef enum {
    StateInitialized,
    StateReady,
    StateRunning,
    StateStandby,
    StateTerminated,
    StateWait,
    StateTransition,
    StateUnknown
} THREAD_STATE;

typedef enum _THREADINFOCLASS {
    ThreadBasicInformation,
    ThreadTimes,
    ThreadPriority,
    ThreadBasePriority,
    ThreadAffinityMask,
    ThreadImpersonationToken,
    ThreadDescriptorTableEntry,
    ThreadEnableAlignmentFaultFixup,
    ThreadEventPair_Reusable,
    ThreadQuerySetWin32StartAddress,
    ThreadZeroTlsCell,
    ThreadPerformanceCount,
    ThreadAmILastThread,
    ThreadIdealProcessor,
    ThreadPriorityBoost,
    ThreadSetTlsArrayAddress,
    ThreadIsIoPending,
    ThreadHideFromDebugger,
    ThreadBreakOnTermination,
    ThreadSwitchLegacyState,
    ThreadIsTerminated,
    ThreadLastSystemCall,
    ThreadIoPriority,
    ThreadCycleTime,
    ThreadPagePriority,
    ThreadActualBasePriority,
    ThreadTebInformation,
    ThreadCSwitchMon,
    ThreadCSwitchPmu,
    ThreadWow64Context,
    ThreadGroupInformation,
    ThreadUmsInformation,
    ThreadCounterProfiling,
    ThreadIdealProcessorEx,
    MaxThreadInfoClass
} THREADINFOCLASS;

typedef struct _CLIENT_ID
{
    PVOID UniqueProcess;
    PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef struct _SYSTEM_THREAD
{
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    LONG Priority;
    LONG BasePriority;
    ULONG ContextSwitches;
    THREAD_STATE ThreadState;
} SYSTEM_THREAD, *PSYSTEM_THREAD;

typedef NTSTATUS (NTAPI *pNtAllocateVirtualMemory)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG_PTR ZeroBits,
    PSIZE_T RegionSize,
    ULONG AllocationType,
    ULONG Protect
);

typedef NTSTATUS(NTAPI *pNtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength OPTIONAL
    );
main.cpp
Code:
#include "stdafx.h"

pNtAllocateVirtualMemory fNtAllocateVirtualMemory;
pNtQuerySystemInformation fNtQuerySystemInformation;

HANDLE GetProcessId2(
    WCHAR *ProcessName
)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    HANDLE ProcessId = 0;
    SYSTEM_PROCESS_INFORMATION *SystemProcessInfo = { 0 };
    PVOID BufferMemory = 0;
    ULONG ReturnLength = 0;
    SIZE_T RegionSize = 0;

    // Call one with NULL params to get the right length
    NtStatus = fNtQuerySystemInformation(SystemProcessInformation,
        NULL,
        NULL,
        &ReturnLength);

    if (!ReturnLength)
    {
        return 0;
    }

    RegionSize = (SIZE_T)ReturnLength;

    NtStatus = fNtAllocateVirtualMemory(NtCurrentProcess(),
        &BufferMemory,
        NULL,
        &RegionSize,
        MEM_RESERVE | MEM_COMMIT,
        PAGE_READWRITE);

    if (!NT_SUCCESS(NtStatus))
    {
        return 0;
    }

    SystemProcessInfo = (SYSTEM_PROCESS_INFORMATION*)BufferMemory;

    // call second time with the right length to get the processes snapshot
    NtStatus = fNtQuerySystemInformation(SystemProcessInformation,
        SystemProcessInfo,
        RegionSize,
        NULL);

    if (NT_SUCCESS(NtStatus))
    {
        while (SystemProcessInfo->NextEntryOffset)
        {

            //
            //    Process ID 0 - 4 you do not need, so by skipping first it doesn't matter anyway
            //

            // move to the next entry
            SystemProcessInfo = (SYSTEM_PROCESS_INFORMATION*)((UCHAR*)SystemProcessInfo + SystemProcessInfo->NextEntryOffset);

            if (!wcscmp(SystemProcessInfo->ImageFileName.Buffer, ProcessName))
            {

                //
                //    The structure for SYSTEM_PROCESS_INFORMATION has an entry for UniqueProcessId (type HANDLE) which is for the Process Id... we can return that
                //

                ProcessId = SystemProcessInfo->UniqueProcessId;
                goto cleanup;
            }
        }
    }

cleanup:
    VirtualFree(BufferMemory, NULL, MEM_RELEASE);

    return ProcessId;
}

int main()
{
    fNtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtAllocateVirtualMemory");
    fNtQuerySystemInformation = (pNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
    if (fNtAllocateVirtualMemory)
    {
        DWORD dwProcessId = (DWORD)GetProcessId2(L"notepad.exe");
    }

    return 0;
}
For more information: NtQuerySystemInformation function (Windows)
[NtQuerySystemInformation may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]
Be wary of that quote for stability purposes.

Thanks for reading.
 
D

Deleted member 65228

Guest
#2
For the record, NtQuerySystemInformation can be used for anti-rootkit purposes. You can call ZwQuerySystemInformation in kernel-mode to get a list of the processes, then you can get a list in user-mode and compare the lists. If there are processes found on one list and not the other, you can do further inspection to try and identify if the process is actually being hidden by a rootkit component or not (e.g. a user-mode rootkit may hook NtQuerySystemInformation after injecting code into processes, but the scope of this interception is not valid for kernel-mode, therefore the rootkit is bypassed and the process it is trying to hide is caught out).

Even in user-mode though, if the Win32 APIs for process enumeration happen to be hooked and you use NtQuerySystemInformation, you'll still catch out the hidden process. You can do checks on the function prologue to see if it has been hooked via inline methods (detouring the function - modifying the instructions in memory), check the IAT address in comparison with the address from the EAT (and also the EAT validation). A simpler way would be to just use NtQuerySystemInformation normally like in the demonstrated code, and then to re-call it again using system calls (prevents chances of interception) and then compare if it is user-mode detection scope only.

There are many more tricks similar to these for finding hidden processes... Maybe it would be a nice idea for an anti-rootkit thread listing more with examples in the future.
 
Forgot your password?