内核下枚举进程 (二)ZwQuerySystemInformation

说明: SYSTEM_INFORMATION_CLASS 的5号功能枚举进程信息。其是这个函数对应着ring3下的 NtQuerySystemInformation,但msdn上说win8以后ZwQuerySystemInformation函数已经不可用,本人也没有在win8下测试过。留给读者自己实验吧。顺便罗嗦一下,不像ring3下使用此函数是要先LoadLibrary,然后GetProcAddress。内核下就简单了,直接声明一下就可以用了(就是这个宏:NTSYSAPI),下面直接附上代码:

#include <ntddk.h>

#define SystemProcessesAndThreadsInformation 5

typedef struct _SYSTEM_PROCESSES
{
	ULONG NextEntryDelta;
	ULONG ThreadCount;
	ULONG Reserved[6];
	LARGE_INTEGER CreateTime;
	LARGE_INTEGER UserTime;
	LARGE_INTEGER KernelTime;
	UNICODE_STRING ProcessName;
	KPRIORITY BasePriority;
	ULONG ProcessId;
	ULONG InheritedFromProcessId;
	ULONG HandleCount;
	ULONG Reserved2[2];
	VM_COUNTERS VmCounters;
	IO_COUNTERS IoCounters;
} _SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;

NTSTATUS EnumSystemProcess( );


NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
	UNREFERENCED_PARAMETER(DriverObject);
	UNREFERENCED_PARAMETER(RegistryPath);
	NTSTATUS status = STATUS_SUCCESS;
	status  = EnumSystemProcess( );
	return status;
}

NTSTATUS EnumSystemProcess( )
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	*pRet = FALSE;

	PSYSTEM_PROCESSES pProcessInfo = NULL;
	PSYSTEM_PROCESSES pTemp = NULL;//这个留作以后释放指针的时候用。
	ULONG ulNeededSize;
	ULONG ulNextOffset;

	if (NULL == pProcess)
	{
		return status;
	}
        //第一次使用肯定是缓冲区不够,不过本人在极少数的情况下第二次也会出现不够,所以用while循环
	status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation , pProcessInfo, 0, &ulNeededSize);
	while (STATUS_INFO_LENGTH_MISMATCH == status)
	{
		pProcessInfo = ExAllocatePoolWithTag(NonPagedPool, ulNeededSize, '1aes');
		pTemp = pProcessInfo;
		if (NULL == pProcessInfo)
		{
			KdPrint(("[allocatePoolWithTag] failed"));
			return status;
		}
		status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation , pProcessInfo, ulNeededSize, &ulNeededSize);
	}
	if (NT_SUCCESS(status))
	{
		KdPrint(("[ZwQuerySystemInformation]success bufferSize:%x", ulNeededSize));
	}
        else
        {
               KdPrint(("[error]:++++%d", status));
               return status;
        }

	do
	{
		KdPrint(("[imageName Buffer]:%08x", pProcessInfo->ProcessName.Buffer));
		
		if (MmIsAddressValid(pProcessInfo->ProcessName.Buffer) && NULL != pProcessInfo)
		{
			KdPrint(("[ProcessID]:%d , [imageName]:%ws", pProcessInfo->ProcessId, pProcessInfo->ProcessName.Buffer));
		}
		
		ulNextOffset = pProcessInfo->NextEntryDelta;
		pProcessInfo = (PSYSTEM_PROCESSES)((PUCHAR)pProcessInfo + pProcessInfo->NextEntryDelta);

	} while (ulNextOffset != 0);

	ExFreePoolWithTag(pTemp, '1aes');

	return status;
}

 DbgView查看输出:
内核下枚举进程 (二)ZwQuerySystemInformation

分析:win7 x86下运行可得到上面的结果,从结果从可以看在进程链表中,链表的第一个节点是无效的,所以应该事先判断内存的有效性,在进行输出,当时就应为这个问题蓝屏了好多次。害的真惨!!!

后记:这里补充一下 SYSTEM_INFORMATION_CLASS 结构:

typedef enum _SYSTEM_INFORMATION_CLASS {

    SystemBasicInformation,
    SystemProcessorInformation,
    SystemPerformanceInformation,
    SystemTimeOfDayInformation,
    SystemPathInformation,
    SystemProcessInformation,   //5号功能
    SystemCallCountInformation,
    SystemDeviceInformation,
    SystemProcessorPerformanceInformation,
    SystemFlagsInformation,
    SystemCallTimeInformation,
    SystemModuleInformation,
    SystemLocksInformation,
    SystemStackTraceInformation,
    SystemPagedPoolInformation,
    SystemNonPagedPoolInformation,
    SystemHandleInformation,
    SystemObjectInformation,
    SystemPageFileInformation,
    SystemVdmInstemulInformation,
    SystemVdmBopInformation,
    SystemFileCacheInformation,
    SystemPoolTagInformation,
    SystemInterruptInformation,
    SystemDpcBehaviorInformation,
    SystemFullMemoryInformation,
    SystemLoadGdiDriverInformation,
    SystemUnloadGdiDriverInformation,
    SystemTimeAdjustmentInformation,
    SystemSummaryMemoryInformation,
    SystemNextEventIdInformation,
    SystemEventIdsInformation,
    SystemCrashDumpInformation,
    SystemExceptionInformation,
    SystemCrashDumpStateInformation,
    SystemKernelDebuggerInformation,
    SystemContextSwitchInformation,
    SystemRegistryQuotaInformation,
    SystemExtendServiceTableInformation,
    SystemPrioritySeperation,
    SystemPlugPlayBusInformation,
    SystemDockInformation,
    SystemPowerInformation,
    SystemProcessorSpeedInformation,
    SystemCurrentTimeZoneInformation,
    SystemLookasideInformation

} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;

枚举进程常用5号功能,5号功能结构对应的结构中有很多信息,包括进程Id,父进程名等重要信息,但5号功能的结构有很多种写法,
下面说一下我所知道的三种写法:

1)第一种:
typedef struct _SYSTEM_PROCESS_INFORMATION {
	ULONG NextEntryOffset;      //下一个结构的偏移量,最后一个偏移量为0
	ULONG NumberOfThreads;
	LARGE_INTEGER SpareLi1;
	LARGE_INTEGER SpareLi2;
	LARGE_INTEGER SpareLi3;
	LARGE_INTEGER CreateTime;
	LARGE_INTEGER UserTime;
	LARGE_INTEGER KernelTime;
	UNICODE_STRING ImageName;     //进程名
	KPRIORITY BasePriority;
	HANDLE UniqueProcessId;               //进程ID
	HANDLE InheritedFromUniqueProcessId;   //父进程ID
	ULONG HandleCount;
	ULONG SessionId;       //会话ID                    
	ULONG_PTR PageDirectoryBase;
	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_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;


1)第二种
typedef struct _SYSTEM_PROCESSES
{
	ULONG NextEntryDelta;
	ULONG ThreadCount;
	ULONG Reserved[6];
	LARGE_INTEGER CreateTime;
	LARGE_INTEGER UserTime;
	LARGE_INTEGER KernelTime;
	UNICODE_STRING ProcessName;
	KPRIORITY BasePriority;
	ULONG ProcessId;
	ULONG InheritedFromProcessId;
	ULONG HandleCount;
	ULONG Reserved2[2];
	VM_COUNTERS VmCounters;
	IO_COUNTERS IoCounters;
} _SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;

3)第三种
typedef struct _SYSTEM_THREAD {
	LARGE_INTEGER           KernelTime;
	LARGE_INTEGER           UserTime;
	LARGE_INTEGER           CreateTime;
	ULONG                   WaitTime;
	PVOID                   StartAddress;
	CLIENT_ID               ClientId;
	KPRIORITY               Priority;
	LONG                    BasePriority;
	ULONG                   ContextSwitchCount;
	ULONG                   State;
	KWAIT_REASON            WaitReason;
} SYSTEM_THREAD, *PSYSTEM_THREAD;//

typedef struct _SYSTEM_PROCESS_INFORMATION {
	ULONG                   NextEntryOffset; //NextEntryDelta 构成结构序列的偏移量
	ULONG                   NumberOfThreads; //线程数目
	LARGE_INTEGER           Reserved[3];
	LARGE_INTEGER           CreateTime;   //创建时间
	LARGE_INTEGER           UserTime;     //用户模式(Ring 3)的CPU时间
	LARGE_INTEGER           KernelTime;   //内核模式(Ring 0)的CPU时间
	UNICODE_STRING          ImageName;    //进程名称
	KPRIORITY               BasePriority; //进程优先权
	HANDLE                  ProcessId;    //ULONG UniqueProcessId 进程标识符
	HANDLE                  InheritedFromProcessId; //父进程的标识符
	ULONG                   HandleCount; //句柄数目
	ULONG                   Reserved2[2];
	ULONG                   PrivatePageCount;
	VM_COUNTERS             VirtualMemoryCounters; //虚拟存储器的结构
	IO_COUNTERS             IoCounters; //IO计数结构
	SYSTEM_THREAD           Threads[1]; //进程相关线程的结构数组
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

其实这三种结构是相同的,用上面的任意一种代替5号功能的结构都可以实现枚举,本人亲自试验过,没有出现任何问题。
就看个人喜欢用哪一个了,说实话本人喜欢用第一个。