在 Windows 操作系统中,了解正在运行的进程的信息对于系统管理和性能优化至关重要。通过遍历系统进程信息,我们可以获取进程的 ID、名称、线程数、句柄数以及各种性能指标,从而帮助我们了解系统的运行状况并进行必要的调整和优化。本文将详细介绍一种通过调用 Windows 系统API来遍历系统进程信息的技术,同时提供一段用 C++ 编写的示例代码。
Windows 操作系统提供了一系列API函数,可以用于获取系统状态和信息。其中之一是 NtQuerySystemInformation 函数,该函数可以用来获取各种系统信息,包括进程信息。通过调用 NtQuerySystemInformation 函数,并将系统信息结构体填充到提供的缓冲区中,我们可以获得系统中运行的所有进程的信息。
在示例代码中,首先定义了一系列结构体,用于存储进程和线程的详细信息。然后通过调用 NtQuerySystemInformation 函数来获取系统进程信息,遍历返回的进程信息结构体,逐个获取每个进程的信息,包括进程名、进程ID、句柄数、线程数等,并在控制台输出。
提权后通过 NtQueryInformationThread 获取线程实际起始地址,通过 GetMappedFileName 获取所在模块路径。
示例代码中使用了以下关键技术:
以下是示例代码中用到的关键部分:
#include
#include
#include
#pragma comment(lib, "psapi.lib")
#ifdef _WIN64
typedef ULONG64 KPRIORITY;
#else
typedef LONG KPRIORITY;
#endif
#define SystemProcessInformation 5 // 功能号
#ifdef _M_IX86
typedef struct _CLIENT_ID
{
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
#endif // x86模式下
#ifdef _M_X64
typedef struct _CLIENT_ID
{
ULONG64 UniqueProcess;
ULONG64 UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
#endif // x64模式下
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef enum _KWAIT_REASON
{
Executive = 0,
FreePage = 1,
PageIn = 2,
PoolAllocation = 3,
DelayExecution = 4,
Suspended = 5,
UserRequest = 6,
WrExecutive = 7,
WrFreePage = 8,
WrPageIn = 9,
WrPoolAllocation = 10,
WrDelayExecution = 11,
WrSuspended = 12,
WrUserRequest = 13,
WrEventPair = 14,
WrQueue = 15,
WrLpcReceive = 16,
WrLpcReply = 17,
WrVirtualMemory = 18,
WrPageOut = 19,
WrRendezvous = 20,
Spare2 = 21,
Spare3 = 22,
Spare4 = 23,
Spare5 = 24,
WrCalloutStack = 25,
WrKernel = 26,
WrResource = 27,
WrPushLock = 28,
WrMutex = 29,
WrQuantumEnd = 30,
WrDispatchInt = 31,
WrPreempted = 32,
WrYieldExecution = 33,
WrFastMutex = 34,
WrGuardedMutex = 35,
WrRundown = 36,
MaximumWaitReason = 37
} KWAIT_REASON;
typedef enum _THREADINFOCLASS {
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger,
ThreadBreakOnTermination,
MaxThreadInfoClass
}THREADINFOCLASS;
// 线程结构体
typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
LONG BasePriority;
ULONG ContextSwitches;
ULONG ThreadState;
KWAIT_REASON WaitReason;
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;
// 进程结构体
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER WorkingSetPrivateSize; // since VISTA
ULONG HardFaultCount; // since WIN7
ULONG NumberOfThreadsHighWatermark; // since WIN7
ULONGLONG CycleTime; // since WIN7
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG_PTR UniqueProcessKey; // since VISTA (requires SystemExtendedProcessInformation)
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
//SYSTEM_THREAD_INFORMATION Threads[1];
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;
typedef struct _THREAD_BASIC_INFORMATION {
LONG ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
LONG AffinityMask;
LONG Priority;
LONG BasePriority;
}THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
// 定义函数声明
typedef NTSTATUS (WINAPI* __NtQuerySystemInformation) (
UINT systemInformation,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (WINAPI* __NtQueryInformationThread)(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
BOOL QueryProcessInformationCallAll() {
ULONG cbSize = sizeof(SYSTEM_PROCESS_INFORMATION);
__NtQuerySystemInformation NtQuerySystemInformation = NULL;
__NtQueryInformationThread NtQueryInformationThread = NULL;
PSYSTEM_PROCESS_INFORMATION sysPSIEntry = NULL,
sysForwardLinks = NULL;
HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
if (hNtDll == NULL)
{
wprintf(L"LoadLibrary Failed.\n");
return FALSE;
}
NtQuerySystemInformation = (__NtQuerySystemInformation)GetProcAddress(
hNtDll, "NtQuerySystemInformation");
NtQueryInformationThread = (__NtQueryInformationThread)GetProcAddress(
hNtDll, "NtQueryInformationThread");
if (NtQuerySystemInformation == NULL || NtQueryInformationThread == NULL)
{
wprintf(L"GetProcAddress Failed.\n");
return FALSE;
}
// 调用 NtQuerySystemInformation 查询进程信息
NTSTATUS status = 0;
status = NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &cbSize);
if (status == 0xC0000004)
{
sysPSIEntry = (PSYSTEM_PROCESS_INFORMATION)malloc(cbSize);
status = NtQuerySystemInformation(SystemProcessInformation, sysPSIEntry, cbSize, &cbSize);
if (status)
free(sysPSIEntry);
}
// 拷贝结构体指针副本
sysForwardLinks = sysPSIEntry;
HANDLE hThread = NULL;
HANDLE hProcess = NULL;
LPVOID lpStartAddress = NULL;
WCHAR wsFileName[256] = { 0 };
THREAD_BASIC_INFORMATION tbi = { 0 };
PSYSTEM_THREAD_INFORMATION threadInfo = nullptr;
DWORD curThreadIndex = 0;
// 使用指针副本遍历进程信息
while (true)
{
if (sysForwardLinks->ImageName.Buffer != NULL)
{
wprintf(L"进程名:\t%s \t进程ID:%I64u \t句柄总数:%u \t线程总数:%u \n",
sysForwardLinks->ImageName.Buffer, (UINT64)sysForwardLinks->UniqueProcessId,
sysForwardLinks->HandleCount, sysForwardLinks->NumberOfThreads);
// 打印线程信息
threadInfo = (PSYSTEM_THREAD_INFORMATION)((UINT64)sysForwardLinks + sizeof(SYSTEM_PROCESS_INFORMATION));
hThread = NULL; hProcess = NULL; curThreadIndex = 0;
for (; curThreadIndex < sysForwardLinks->NumberOfThreads; ++curThreadIndex)
{
lpStartAddress = NULL;
memset(wsFileName, 0, sizeof(wsFileName));
// 打开线程
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)threadInfo->ClientId.UniqueThread);
if (hThread == NULL)
{
wprintf(L"打开线程 %lld 失败。\n", threadInfo->ClientId.UniqueThread);
}
else {
// 获取线程入口地址
NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress,
&lpStartAddress, sizeof(lpStartAddress), NULL);
// 获取线程所在模块
tbi = { 0 };
NtQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)sysForwardLinks->UniqueProcessId);
if (hProcess != NULL)
{
// 检查入口地址是否位于某模块中
if (0 == GetMappedFileName(hProcess, lpStartAddress,
wsFileName, sizeof(wsFileName) / sizeof(*wsFileName)))
{
wprintf(L"没有足够的权限来获取模块路径。\n");
}
CloseHandle(hProcess);
}
else {
wprintf(L"打开进程 %lld 失败。\n", (UINT64)sysForwardLinks->UniqueProcessId);
}
CloseHandle(hThread);
}
#ifdef _WIN64
wprintf(L"\t线程ID:%I64u \t起始地址:0x%I64X \t位于模块:[%ws] \t线程的状态码:%u\n",
threadInfo->ClientId.UniqueThread,
(UINT64)lpStartAddress,
wsFileName,
threadInfo->ThreadState);
#else
wprintf(L"\t线程ID:%d \t起始地址:0x%I32X \t位于模块:[%ws] \t线程的状态码:%u\n",
threadInfo->ClientId.UniqueThread,
(UINT)lpStartAddress,
wsFileName,
threadInfo->ThreadState);
#endif
threadInfo += 1;
}
wprintf(L"\n");
}
if (sysForwardLinks->NextEntryOffset == 0)
break;
// 指针的加减运算的单位是根据所指向数据类型大小的。
// 字节指针就是1,所以加减运算没问题。
// 这里是结构体指针,所以必须转成数字类型再运算。
sysForwardLinks = (PSYSTEM_PROCESS_INFORMATION)((UINT64)sysForwardLinks + sysForwardLinks->NextEntryOffset);
}
// 释放内存
free(sysPSIEntry);
sysPSIEntry = NULL;
sysForwardLinks = NULL;
return TRUE;
}
int main()
{
setlocale(LC_ALL, ".utf8");// 转换控制台代码页编码为 UTF-8
QueryProcessInformationCallAll();
return 0;
}
测试结果截图:
通过遍历系统进程信息,我们可以了解系统中运行的各种进程的详细信息,从而更好地进行系统管理和性能优化。本文介绍了一种通过调用 Windows 系统API来获取并遍历系统进程信息的技术,并提供了一段示例代码以帮助读者理解该技术的实现方法。希望本文对于对 Windows 系统进程信息感兴趣的读者有所帮助。
发布于:2024.02.07,更新于:2024.02.07