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);
            }
        }
    }

 

上一篇:C#获取时间戳


下一篇:Windows 驱动开发2 链表的数据结构