Processes and Threads Sample

This sample demonstrates how to list all processes and their threads on Windows NT using the undocumented ZwQuerySystemInformation function. This function, which prototype is shown below, provides access to a wide variety of system information.  http://www.alexfedotov.com/code/threads.zip

NTSTATUS ZwQuerySystemInformation(
    IN ULONG SystemInformationClass,    // information class
    IN OUT PVOID SystemInformation,     // information buffer
    IN ULONG SystemInformationLength,   // size of information buffer
    OUT PULONG ReturnLength OPTIONAL    // receives information length
    );

The first parameter, SystemInformationClass, specifies the type of information to retrieve. To obtain information about processes and threads, this parameter should be set to 5.

The second parameter, SystemInformation, should point to a buffer into which the system will store the requested information. For processes and threads, the information is returned as an array of SYSTEM_PROCESS_INFORMATION structures, one structure for each process in the system. The SYSTEM_PROCESS_INFORMATION structure contains most of the information that is displayed by the Windows NT Task Manager and also includes an array of SYSTEM_THREAD_INFORMATION structures that describe threads of the process.

typedef struct _SYSTEM_THREAD_INFORMATION {
    LARGE_INTEGER   KernelTime;             // time spent in kernel mode
    LARGE_INTEGER   UserTime;               // time spent in user mode
    LARGE_INTEGER   CreateTime;             // thread creation time
    ULONG           WaitTime;               // wait time
    PVOID           StartAddress;           // start address
    CLIENT_ID       ClientId;               // thread and process IDs
    KPRIORITY       Priority;               // dynamic priority
    KPRIORITY       BasePriority;           // base priority
    ULONG           ContextSwitchCount;     // number of context switches
    LONG            State;                  // current state
    LONG            WaitReason;             // wait reason
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG           NextEntryDelta;         // offset to the next entry
    ULONG           ThreadCount;            // number of threads
    ULONG           Reserved1[6];           // reserved
    LARGE_INTEGER   CreateTime;             // process creation time
    LARGE_INTEGER   UserTime;               // time spent in user mode
    LARGE_INTEGER   KernelTime;             // time spent in kernel mode
    UNICODE_STRING  ProcessName;            // process name
    KPRIORITY       BasePriority;           // base process priority
    ULONG           ProcessId;              // process identifier
    ULONG           InheritedFromProcessId; // parent process identifier
    ULONG           HandleCount;            // number of handles
    ULONG           Reserved2[2];           // reserved
    VM_COUNTERS     VmCounters;             // virtual memory counters
#if _WIN32_WINNT >= 0x500
    IO_COUNTERS     IoCounters;             // i/o counters
#endif
    SYSTEM_THREAD_INFORMATION Threads[1];   // threads
} SYSTEM_PROCESS_INFORMATION, * SYSTEM_PROCESS_INFORMATION;

Note that the size of the SYSTEM_PROCESS_INFORMATION structure differs between Windows NT 4.0 and Windows 2000. This is a perfect example why you should not use undocumented functions.

Below is the source code of the sample application. The application detemines the operating system version to handle correctly differences in the process information structure. Then the application requests processes and threads information and displays it on the screen.

int _tmain(
    int argc, 
    _TCHAR * argv[]
    )
{
    // determine operating system version
    OSVERSIONINFO osvi;
    osvi.dwOSVersionInfoSize = sizeof(osvi);
    GetVersionEx(&osvi);

    ULONG cbBuffer = 0x8000;
    LPVOID pBuffer = NULL;
    NTSTATUS Status;

    // it is difficult to detemine a priory which size of the
    // buffer will be enough to retrieve all information, so we
    // start with 32K buffer and increase its size until we get
    // the information successfully
    do
    {
        pBuffer = malloc(cbBuffer);
        if (pBuffer == NULL)
        {
            _tprintf(_T("Not enough memory/n"));
            return 1;
        }

        Status = ZwQuerySystemInformation(
                    SystemProcessesAndThreadsInformation,
                    pBuffer, cbBuffer, NULL);

        if (Status == STATUS_INFO_LENGTH_MISMATCH)
        {
            free(pBuffer);
            cbBuffer *= 2;
        }
        else if (!NT_SUCCESS(Status))
        {
            _tprintf(_T("ZwQuerySystemInformation failed with")
                     _T("status 0x%08X/n"), Status);

            free(pBuffer);
            return 1;
        }
    }
    while (Status == STATUS_INFO_LENGTH_MISMATCH);

    PSYSTEM_PROCESS_INFORMATION pInfo = 
        (PSYSTEM_PROCESS_INFORMATION)pBuffer;

    for (;;)
    {
        PCWSTR pszProcessName = pInfo->ProcessName.Buffer;
        if (pszProcessName == NULL)
            pszProcessName = L"Idle";

        _tprintf(_T("ProcessID: %d (%ls)/n"), pInfo->ProcessId,
                 pszProcessName);

        ULONG ThreadCount = pInfo->ThreadCount;
        PSYSTEM_THREAD_INFORMATION pThreads;
        
        if (osvi.dwMajorVersion < 5)
            pThreads = ((PSYSTEM_PROCESS_INFORMATION_NT4)pInfo)->Threads;
        else
            pThreads = pInfo->Threads;

        for (ULONG i = 0; i < ThreadCount; i++)
        {
            DWORD dwThreadId = pThreads[i].ClientId.UniqueThread;
            _tprintf(_T("/tThreadID: %d/n"), dwThreadId);

            TestOpenThread(dwThreadId);
        }

        if (pInfo->NextEntryDelta == 0)
            break;

        // find the address of the next process structure
        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo)
                        + pInfo->NextEntryDelta);
    }

    free(pBuffer);
    return 0;
}

Note: The method described is not the only way how the list of processes and threads can be obtained. Other methods include performance data interface, PSAPI (for the list of processes), Toolhelp32 API (available in Windows 2000 and later) and Windows Management Instrumentation (WMI). It is strongly recommented to use one of documented methods to obtain this information rather than call ZwQuerySystemInformation.

Among other enhancements, a new Win32 API function named OpenThread was added in Windows 2000. This function returns a handle of an existing thread given the thread identifier. This sample shows how OpenThread can be implemented on earlier versions of Windows NT using the undocumented ZwOpenThread function.

Below is the source code of the OpenThread replacement. I won't be surprised if the actual OpenThread code doesn't differ much from what you see here.

HANDLE OpenThread(
    DWORD dwDesiredAccess,  // access right
    BOOL bInheritHandle,    // handle inheritance option
    DWORD dwThreadId        // thread identifier
    )
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    CLIENT_ID ClientId;
    HANDLE hThread;
    NTSTATUS Status;
    
    memset(&ObjectAttributes, 0, sizeof(ObjectAttributes));
    
    if (bInheritHandle)
        ObjectAttributes.Attributes = OBJ_INHERIT;
    
    ClientId.UniqueProcess = NULL;
    ClientId.UniqueThread = (HANDLE)dwThreadId;
    
    Status = ZwOpenThread(&hThread, dwDesiredAccess,
                          &ObjectAttributes, &ClientId);
              
    if (!NT_SUCCESS(Status))
    {        
        SetLastError(LsaNtStatusToWinError(Status));
        return NULL;
    }
    
    return hThread;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值