WinAPI ZwQuerySystemInformation的简要分析
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, //枚举类型,用户输入值选择要查询的信息
_Inout_ PVOID SystemInformation, //缓冲区,用来接收信息的
_In_ ULONG SystemInformationLength, //填写缓冲区的大小
_Out_opt_ PULONG ReturnLength //返回信息的长度
);
NTSTATUS 返回状态码
//#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) //运行成功
//#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) //运行失败
//#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) //没碰到过,我也不知道
//#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003L) //第一个参数值输入错误
//#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)// 缓冲区太小了,放不下
//注意此函数是系统标准调用,类型为NTSTATUS (WINAPI *)(SYSTEM_INFORMATION_CLASS,PVOID,ULONG,PULONG)
因为SYSTEM_INFORMATION_CLASS是一个枚举类型所以类型又可为NTSTATUS (WINAPI *)(UINT,PVOID,ULONG,PULONG)
下面通过一个查询系统进程的案例来演示一下:
第一步:导入ZwQuerySystemInformation
/** 名称:getAPIAddress 功能: 根据模块名称和函数名称获取函数地址 参数: TCHAR * moduleName 模块名称 TCHAR * funcName 函数名称 返回值:return 1 represent moduleName is wrong return 0 represent funcName is wrong **/ FARPROC getAPIAddress(TCHAR * moduleName,TCHAR * wFuncName){ char funcName[MAX_PATH] = {0,}; WideCharToMultiByte(CP_ACP, NULL,(LPCWSTR)wFuncName,-1,funcName,MAX_PATH,0,0); HMODULE hKer; hKer = GetModuleHandle(moduleName); if(!hKer) return (FARPROC)-1; return GetProcAddress(hKer,funcName); } void main(){ //导入ZwQuerySystemInformation函数 long (WINAPI *ZwQuerySystemInformation)(UINT, PVOID, ULONG ,PULONG ); //NTSTATUS是long的别名,所以直接用long也行 ZwQuerySystemInformation =( long (WINAPI *)(UINT, PVOID, ULONG ,PULONG ))getAPIAddress(L"ntdll.dll",L"NtQuerySystemInformation"); }
第二步: 查询进程信息长度
1.上面说过 这个函数返回什么信息是由第一个枚举类型的参数SYSTEM_INFORMATION_CLASS 决定的,这个枚举的详细信息见下
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation, // 0 Y N
SystemProcessorInformation, // 1 Y N
SystemPerformanceInformation, // 2 Y N
SystemTimeOfDayInformation, // 3 Y N
SystemNotImplemented1, // 4 Y N
SystemProcessesAndThreadsInformation, // 5 Y N //查询系统进程和线程信息
SystemCallCounts, // 6 Y N
SystemConfigurationInformation, // 7 Y N
SystemProcessorTimes, // 8 Y N
SystemGlobalFlag, // 9 Y Y
SystemNotImplemented2, // 10 Y N
SystemModuleInformation, // 11 Y N
SystemLockInformation, // 12 Y N
SystemNotImplemented3, // 13 Y N
SystemNotImplemented4, // 14 Y N
SystemNotImplemented5, // 15 Y N
SystemHandleInformation, // 16 Y N
SystemObjectInformation, // 17 Y N
SystemPagefileInformation, // 18 Y N
SystemInstructionEmulationCounts, // 19 Y N
SystemInvalidInfoClass1, // 20
SystemCacheInformation, // 21 Y Y
SystemPoolTagInformation, // 22 Y N
SystemProcessorStatistics, // 23 Y N
SystemDpcInformation, // 24 Y Y
SystemNotImplemented6, // 25 Y N
SystemLoadImage, // 26 N Y
SystemUnloadImage, // 27 N Y
SystemTimeAdjustment, // 28 Y Y
SystemNotImplemented7, // 29 Y N
SystemNotImplemented8, // 30 Y N
SystemNotImplemented9, // 31 Y N
SystemCrashDumpInformation, // 32 Y N
SystemExceptionInformation, // 33 Y N
SystemCrashDumpStateInformation, // 34 Y Y/N
SystemKernelDebuggerInformation, // 35 Y N
SystemContextSwitchInformation, // 36 Y N
SystemRegistryQuotaInformation, // 37 Y Y
SystemLoadAndCallImage, // 38 N Y
SystemPrioritySeparation, // 39 N Y
SystemNotImplemented10, // 40 Y N
SystemNotImplemented11, // 41 Y N
SystemInvalidInfoClass2, // 42
SystemInvalidInfoClass3, // 43
SystemTimeZoneInformation, // 44 Y N
SystemLookasideInformation, // 45 Y N
SystemSetTimeSlipEvent, // 46 N Y
SystemCreateSession, // 47 N Y
SystemDeleteSession, // 48 N Y
SystemInvalidInfoClass4, // 49
SystemRangeStartInformation, // 50 Y N
SystemVerifierInformation, // 51 Y Y
SystemAddVerifier, // 52 N Y
SystemSessionProcessesInformation // 53 Y N
} SYSTEM_INFORMATION_CLASS;
由上可知,要想查询系统进程信息,应该选5.但是我们并不知道进程信息有多大(因为系统运行的进程数量是不固定的),所以我们把第三个参数缓冲区大小填0,这样函数就会把进程信息长度返回在第四个参数 代码如下
DWORD len; long result; //获取数据长度 result = ZwQuerySystemInformation((UINT)5,NULL,0,&len); //第一个参数因为是枚举类型的5号位置,所以填5 //第二个参数填NULL,因为我们现在还不用接收数据,填NULL就行了 //第三个参数填0,故意填0,函数就会把信息长度返回在第四个参数了。
//正常的话result会返回0xC0000004L
第三步:获取进程信息
获取到长度后,分配相应长度缓冲区用来接收信息
//len为前面获取到的信息长度 BYTE * pBuf; pBuf =new BYTE[len];
然后正常调用函数就行了
result = ZwQuerySystemInformation(5,pBuf,len,&len); //正常的话,result会返回0x00000000L
第四步输出进程名
获取到的进程信息是一个链表结构体。
//因为此函数为不公开的函数,所以没有文档,大部分结构体要自己定义 这个结构体可以在Micorsoft Docs查到,上百度就可以查到官网 //每一个结构体代表了一个进程 typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; //下一个进程结构体的相对位置,用此结构体的地址加上NextEntryOffset就可以得到下一个结构体的地址 ULONG NumberOfThreads; BYTE Reserved1[48]; PVOID Reserved2[3]; //数组第二个就是进程名的字符串指针 HANDLE UniqueProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; } SYSTEM_PROCESS_INFORMATION,*PSYSTEM_PROCESS_INFORMATION; if (result == STATUS_SUCCESS) { PSYSTEM_PROCESS_INFORMATION spi; PSYSTEM_PROCESS_INFORMATION pre = spi = (PSYSTEM_PROCESS_INFORMATION)pBuf; do { printf(" %ws\n", spi->Reserved2[1]);//打印进程名 注意进程名是宽字符 pre = spi; spi = (PSYSTEM_PROCESS_INFORMATION)((ULONG)spi + spi->NextEntryOffset); } while (pre->NextEntryOffset!=0); }
完整代码如下 编译环境 vs2010 字符集为宽字符 已在window7 32位测试通过
#include <stdio.h> #include <windows.h> #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; BYTE Reserved1[48]; PVOID Reserved2[3]; HANDLE UniqueProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; } SYSTEM_PROCESS_INFORMATION,*PSYSTEM_PROCESS_INFORMATION; /** 名称:getAPIAddress 功能: 根据模块名称和函数名称获取函数地址 参数: TCHAR * moduleName 模块名称 TCHAR * funcName 函数名称 返回值:return 1 represent moduleName is wrong return 0 represent funcName is wrong **/ FARPROC getAPIAddress(TCHAR * moduleName,TCHAR * wFuncName){ char funcName[MAX_PATH] = {0,}; WideCharToMultiByte(CP_ACP, NULL,(LPCWSTR)wFuncName,-1,funcName,MAX_PATH,0,0); HMODULE hKer; hKer = GetModuleHandle(moduleName); if(!hKer) return (FARPROC)-1; return GetProcAddress(hKer,funcName); } //typedef LONG NTSTATUS; void main(){ //导入ZwQuerySystemInformation函数 long (WINAPI *ZwQuerySystemInformation)(UINT, PVOID, ULONG ,PULONG ); ZwQuerySystemInformation =( long (WINAPI *)(UINT, PVOID, ULONG ,PULONG ))getAPIAddress(L"ntdll.dll",L"NtQuerySystemInformation"); DWORD len; long result; BYTE * pBuf; //获取数据长度 result = ZwQuerySystemInformation((UINT)5,NULL,0,&len); pBuf =new BYTE[len]; if (result==STATUS_INFO_LENGTH_MISMATCH ) { //获取数据 result = ZwQuerySystemInformation(5,pBuf,len,&len); if (result == STATUS_SUCCESS) { PSYSTEM_PROCESS_INFORMATION spi; PSYSTEM_PROCESS_INFORMATION pre = spi = (PSYSTEM_PROCESS_INFORMATION)pBuf; do { printf(" %ws\n", spi->Reserved2[1]); pre = spi; spi = (PSYSTEM_PROCESS_INFORMATION)((ULONG)spi + spi->NextEntryOffset); } while (pre->NextEntryOffset!=0); } } }