函数ZwQuerySystemInformation小结

函数存在于NTDLL.DLL动态链接库中。NTDLL.DLL负责ring3与ring0之间的通信。当使用子系统方式进行系统调用的时候,ntdll.dll和SSDT会配合使用。
关于ZwQuerySystemInformation这个函数可以用来查询进程信息、内核信息、硬件信息(例如CPU数目)、句柄信息、时间信息等54个系统信息。
该函数的原型是
  1. NTSTATUS WINAPI ZwQuerySystemInformation(  
  2.   
  3.   __in          SYSTEM_INFORMATION_CLASSSystemInformationClass,  
  4.   
  5.   __in_out     PVOIDSystemInformation,  
  6.   
  7.   __in          ULONGSystemInformationLength,  
  8.   
  9.   __out_opt    PULONGReturnLength  
  10.   
  11. );  
NTSTATUS WINAPI ZwQuerySystemInformation(

  __in          SYSTEM_INFORMATION_CLASSSystemInformationClass,

  __in_out     PVOIDSystemInformation,

  __in          ULONGSystemInformationLength,

  __out_opt    PULONGReturnLength

);


至于第一个参数SYSTEM_INFORMATION_CLASS是一个枚举结构。枚举了所有的54个系统信息。该结构在最后将会列举出来。

一、用户模式下的ZwQuerySystemInformation
在用户模式下必须用LoadLibrary与GetProcAddress来获取该函数地址。
代码如下,
  1. 先声明一个函数。  
  2. typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(INSYSTEM_INFORMATION_CLASS,IN OUT PVOID,INULONG,OUTPULONG);  
  3.   
  4. 加载NTDLL.DLL,获取函数地址。  
  5.   
  6. NTQUERYSYSTEMINFORMATIONZwQuerySystemInformation = NULL;  
  7.   
  8. ZwQuerySystemInformation =  
  9.   
  10. (NTQUERYSYSTEMINFORMATION)GetProcAddress(ntdll.dll,"ZwQuerySystemInfromation");  
先声明一个函数。
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(INSYSTEM_INFORMATION_CLASS,IN OUT PVOID,INULONG,OUTPULONG);

加载NTDLL.DLL,获取函数地址。

NTQUERYSYSTEMINFORMATIONZwQuerySystemInformation = NULL;

ZwQuerySystemInformation =

(NTQUERYSYSTEMINFORMATION)GetProcAddress(ntdll.dll,"ZwQuerySystemInfromation");


举例:枚举进程信息
要想获取进程信息,必须使用第二个参数,第二个参数指向一块内存。必须使用参数1中每个系统信息对应的结构体来将该内存进行转换。
假设我们要枚举进程信息,必须使用下列结构,该结构描述了进程名,线程数,指向下一个模块的指针,创建时间等等。结构描述如下:

  1. typedef struct _SYSTEM_PROCESSES    
  2. {    
  3.     ULONG          NextEntryDelta;          //构成结构序列的偏移量;    
  4.     ULONG          ThreadCount;             //线程数目;    
  5.     ULONG          Reserved1[6];               
  6.     LARGE_INTEGER  CreateTime;              //创建时间;    
  7.     LARGE_INTEGER  UserTime;                //用户模式(Ring 3)的CPU时间;    
  8.     LARGE_INTEGER  KernelTime;              //内核模式(Ring 0)的CPU时间;    
  9.     UNICODE_STRING ProcessName;             //进程名称;    
  10.     KPRIORITY      BasePriority;            //进程优先权;    
  11.     HANDLE         ProcessId;               //进程标识符;    
  12.     HANDLE         InheritedFromProcessId;  //父进程的标识符;    
  13.     ULONG          HandleCount;             //句柄数目;    
  14.     ULONG          Reserved2[2];    
  15.     VM_COUNTERS    VmCounters;              //虚拟存储器的结构;    
  16.     IO_COUNTERS    IoCounters;              //IO计数结构;    
  17.     SYSTEM_THREADS Threads[1];              //进程相关线程的结构数组;    
  18. }SYSTEM_PROCESSES,*PSYSTEM_PROCESSES;   
typedef struct _SYSTEM_PROCESSES  
{  
    ULONG          NextEntryDelta;          //构成结构序列的偏移量;  
    ULONG          ThreadCount;             //线程数目;  
    ULONG          Reserved1[6];             
    LARGE_INTEGER  CreateTime;              //创建时间;  
    LARGE_INTEGER  UserTime;                //用户模式(Ring 3)的CPU时间;  
    LARGE_INTEGER  KernelTime;              //内核模式(Ring 0)的CPU时间;  
    UNICODE_STRING ProcessName;             //进程名称;  
    KPRIORITY      BasePriority;            //进程优先权;  
    HANDLE         ProcessId;               //进程标识符;  
    HANDLE         InheritedFromProcessId;  //父进程的标识符;  
    ULONG          HandleCount;             //句柄数目;  
    ULONG          Reserved2[2];  
    VM_COUNTERS    VmCounters;              //虚拟存储器的结构;  
    IO_COUNTERS    IoCounters;              //IO计数结构;  
    SYSTEM_THREADS Threads[1];              //进程相关线程的结构数组;  
}SYSTEM_PROCESSES,*PSYSTEM_PROCESSES; 

 循环程序如下:

  1.  PSYSTEM_PROCESSES psp=NULL;   
  2.   
  3. //先为参数2设为空,dwNeedSize获取保存该结构体的内存大小  
  4. status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &dwNeedSize);   
  5.   
  6. //若用户提供的缓冲区大小不够,则返回STATUS_INFO_LENGTH_MISMATCH,并返回实际需要的缓冲区大小  
  7. if ( status ==STATUS_INFO_LENGTH_MISMATCH ) {     
  8.            pBuffer = new BYTE[dwNeedSize];    
  9.             status =ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, (PVOID)pBuffer,dwNeedSize, NULL);    
  10.            if ( status ==STATUS_SUCCESS )    
  11.             {    
  12.                psp = (PSYSTEM_PROCESSES)pBuffer; //强制转换  
  13.                printf("PID  线程数工作集大小进程名\n");  
  14.                do {    
  15.                    printf("%-4d",psp->ProcessId);  
  16.                    printf(" %3d",psp->ThreadCount);    
  17.                    printf(" %8dKB",psp->VmCounters.WorkingSetSize/1024);  
  18.                    wprintf(L" %s\n",psp->ProcessName.Buffer);  
  19.                    psp = (PSYSTEM_PROCESSES)((ULONG)psp +psp->NextEntryDelta );    
  20.                 }while ( psp->NextEntryDelta != 0 );//循环遍历  
  21.         }  
  22.         delete []pBuffer;    
  23.         pBuffer =NULL;   
  24.   }  
 PSYSTEM_PROCESSES psp=NULL; 

//先为参数2设为空,dwNeedSize获取保存该结构体的内存大小
status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &dwNeedSize); 

//若用户提供的缓冲区大小不够,则返回STATUS_INFO_LENGTH_MISMATCH,并返回实际需要的缓冲区大小
if ( status ==STATUS_INFO_LENGTH_MISMATCH ) {   
           pBuffer = new BYTE[dwNeedSize];  
            status =ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, (PVOID)pBuffer,dwNeedSize, NULL);  
           if ( status ==STATUS_SUCCESS )  
            {  
               psp = (PSYSTEM_PROCESSES)pBuffer; //强制转换
               printf("PID  线程数工作集大小进程名\n");
               do {  
                   printf("%-4d",psp->ProcessId);
                   printf(" %3d",psp->ThreadCount);  
                   printf(" %8dKB",psp->VmCounters.WorkingSetSize/1024);
                   wprintf(L" %s\n",psp->ProcessName.Buffer);
                   psp = (PSYSTEM_PROCESSES)((ULONG)psp +psp->NextEntryDelta );  
                }while ( psp->NextEntryDelta != 0 );//循环遍历
        }
        delete []pBuffer;  
        pBuffer =NULL; 
  }

二、内核模式下的ZwQuerySystemInformation
内核模式下的ZwQuerySystemInformation的地址的获取没有应用层那么麻烦。直接声明一下该函数即可。
  1. NTSYSAPI  
  2.   
  3. NTSTATUS  
  4.   
  5. NTAPI ZwQuerySystemInformation(  
  6.   
  7.             IN ULONG SystemInformationClass,  
  8.   
  9.             IN OUT PVOID SystemInformation,  
  10.   
  11.             IN ULONG SystemInformationLength,  
  12.   
  13.             OUT PULONG ReturnLength);  
NTSYSAPI

NTSTATUS

NTAPI ZwQuerySystemInformation(

            IN ULONG SystemInformationClass,

            IN OUT PVOID SystemInformation,

            IN ULONG SystemInformationLength,

            OUT PULONG ReturnLength);
注意这里的开头使用了一个NTKERNELAPI,这个宏我不知道是干啥的,就到几个群里问了一下,得到了答案,它是在winddk.h这个头文件中声明的,如下:

  1. #if (defined(_NTDRIVER_) || defined(_NTDDK_) || defined(_NTIFS_) || defined(_NTHAL_)) && !defined(_BLDR_)  
  2.   
  3.     #define NTKERNELAPI DECLSPEC_IMPORT         // wdm  
  4.   
  5. #else  
  6.   
  7.     #define NTKERNELAPI  
  8.   
  9. #endif  
#if (defined(_NTDRIVER_) || defined(_NTDDK_) || defined(_NTIFS_) || defined(_NTHAL_)) && !defined(_BLDR_)

    #define NTKERNELAPI DECLSPEC_IMPORT         // wdm

#else

    #define NTKERNELAPI

#endif

函数照上面的方法声明之后就可以直接用了,如下是我的代码,基本和ring3没多大区别:

  1. //  
  2. //  
  3. //    使用ZwQuerySystemInformation函数枚举进程  
  4. //  
  5. //  
  6. VOID  
  7. EnumProcessList1()  
  8. {  
  9.     ULONG cbBuffer = 0x10000;  
  10.     ULONG dwCount  = 0;  
  11.     PVOID pBuffer  = NULL;  
  12.     PSYSTEM_PROCESS_INFORMATION pInfo;  
  13.   
  14.     pBuffer = ExAllocatePool(PagedPool, cbBuffer);  
  15.     // 获取进程信息  
  16.     KdPrint(("We Use ZwQuerySystemInformation!"));  
  17.     ZwQuerySystemInformation(    SystemProcessesAndThreadsInformation,  
  18.                                 pBuffer,  
  19.                                 cbBuffer,  
  20.                                 NULL);  
  21.   
  22.     pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;  
  23.     for( ; ; )  
  24.     {  
  25.         dwCount++;  
  26.         if (pInfo->ProcessId == 0)  
  27.         {  
  28.             KdPrint(("[%6d] System Idle Process", pInfo->ProcessId));  
  29.         }  
  30.         else  
  31.         {  
  32.             KdPrint(("[%6d] %wZ", pInfo->ProcessId, pInfo->ProcessName));  
  33.         }  
  34.   
  35.         if (pInfo->NextEntryDelta == 0)  
  36.         {  
  37.             break;  
  38.         }  
  39.   
  40.         pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);  
  41.     }  
  42.     KdPrint(("ProcessCount = %d", dwCount));  
  43.     ExFreePool(pBuffer);  
  44. }  
//
//
//    使用ZwQuerySystemInformation函数枚举进程
//
//
VOID
EnumProcessList1()
{
    ULONG cbBuffer = 0x10000;
    ULONG dwCount  = 0;
    PVOID pBuffer  = NULL;
    PSYSTEM_PROCESS_INFORMATION pInfo;

    pBuffer = ExAllocatePool(PagedPool, cbBuffer);
    // 获取进程信息
    KdPrint(("We Use ZwQuerySystemInformation!"));
    ZwQuerySystemInformation(    SystemProcessesAndThreadsInformation,
                                pBuffer,
                                cbBuffer,
                                NULL);

    pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
    for( ; ; )
    {
        dwCount++;
        if (pInfo->ProcessId == 0)
        {
            KdPrint(("[%6d] System Idle Process", pInfo->ProcessId));
        }
        else
        {
            KdPrint(("[%6d] %wZ", pInfo->ProcessId, pInfo->ProcessName));
        }

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

        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
    }
    KdPrint(("ProcessCount = %d", dwCount));
    ExFreePool(pBuffer);
}

这是一个C代码程序,该程序是在ring3层写的,主要内容是获取CPU个数,枚举进程,枚举内核模块。该代码是从网上下载的,因为要用到这个函数,所以小小地研究了一下。


------------------------------------------------------------------------------------------------------------------------------------------

简单说,即调用第11号功能,枚举一下内核中已加载的模块。
部分代码如下:
//功能号为11,先获取所需的缓冲区大小
ZwQuerySystemInformation(SystemModuleInformation,NULL,0,&needlen);
//申请内存
ZwAllocateVirtualMemory(NtCurrentProcess(),(PVOID*)&pBuf,0,&needlen,MEM_COMMIT,PAGE_READWRITE);
//再次调用
ZwQuerySystemInformation(SystemModuleInformation,(PVOID)pBuf,truelen,&needlen);
......
//最后,释放内存
ZwFreeVirtualMemory(NtCurrentProcess(),(PVOID*)&pBuf,&needlen,MEM_RELEASE);

突出过程,省略了错误判断,和调用其它的功能时操作并没有什么区别。
关键在返回的内容中,缓冲区pBuf的前四个字节是已加载的模块总数,记为ModuleCnt,接下来就是共有ModuleCnt个元素的模块信息数组了。

  1. //该结构如下:  
  2. typedef struct _SYSTEM_MODULE_INFORMATION {  
  3. ULONG Count;  
  4. SYSTEM_MODULE_INFORMATION_ENTRY Module[1];  
  5. } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;  
  6.   
  7. //模块详细信息结构如下:  
  8. typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {  
  9. HANDLE Section;  
  10. PVOID MappedBase;  
  11. PVOID Base;  
  12. ULONG Size;  
  13. ULONG Flags;  
  14. USHORT LoadOrderIndex;  
  15. USHORT InitOrderIndex;  
  16. USHORT LoadCount;  
  17. USHORT PathLength;  
  18. CHAR ImageName[256];  
  19. } SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;  
//该结构如下:
typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

//模块详细信息结构如下:
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {
HANDLE Section;
PVOID MappedBase;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
一个for循环,循环ModuleCnt次就OK了。
基于此,写了三个简单的函数。

void ShowAllModules(char *pBuf)
{
//函数功能:输出所有模块信息
//参数pBuf:ZwQuerySystemInformation返回的缓冲区首址
PSYSTEM_MODULE_INFORMATION_ENTRY pSysModuleInfo;
DWORD Modcnt=0;
Modcnt=*(DWORD*)pBuf;
pSysModuleInfo=(PSYSTEM_MODULE_INFORMATION_ENTRY)(pBuf+sizeof(DWORD));
for (DWORD i=0;i<Modcnt;i++)
{
   printf("%d\t0x%08X 0x%08X %s\n",pSysModuleInfo->LoadOrderIndex,pSysModuleInfo->Base,pSysModuleInfo->Size,pSysModuleInfo->ImageName);
   pSysModuleInfo++;
}

}
void GetOSKrnlInfo(char *pBuf,DWORD *KernelBase,char *szKrnlPath)
{
//函数功能:返回系统内核(ntoskrnl.exe或ntkrnlpa.exe)的基址和路径
//参数pBuf:ZwQuerySystemInformation返回的缓冲区首址
//参数KernelBase:接收返回的系统内核的基址
//参数szKrnlPath:接收返回的内核文件的路径
PSYSTEM_MODULE_INFORMATION_ENTRY pSysModuleInfo;
DWORD Modcnt=0;
*KernelBase=0;
Modcnt=*(DWORD*)pBuf;
pSysModuleInfo=(PSYSTEM_MODULE_INFORMATION_ENTRY)(pBuf+sizeof(DWORD));
//其实第一个模块就是了,还是验证一下吧
if (strstr((strlwr(pSysModuleInfo->ImageName),pSysModuleInfo->ImageName),"nt"))
{
   *KernelBase=(DWORD)pSysModuleInfo->Base;
   GetSystemDirectory(szKrnlPath,MAX_PATH);
   lstrcat(szKrnlPath,strrchr(pSysModuleInfo->ImageName,'\\'));
}
}


void DetectModule(char *pBuf,DWORD dwAddress,char *ModulePath)
{
//函数功能:找出给定地址所在的模块
//参数pBuf:缓冲区地址,同上
//参数dwAddress:要查询的内核地址
//参数ModulePath:接收返回的模块路径
PSYSTEM_MODULE_INFORMATION_ENTRY pSysModuleInfo;
DWORD Modcnt=0;
Modcnt=*(DWORD*)pBuf;
pSysModuleInfo=(PSYSTEM_MODULE_INFORMATION_ENTRY)(pBuf+sizeof(DWORD));
for (DWORD i=0;i<Modcnt;i++)
{
   if ((dwAddress>=(DWORD)pSysModuleInfo->Base)&&(dwAddress<(DWORD)pSysModuleInfo->Base+pSysModuleInfo->Size))
   {
    lstrcpy(ModulePath,pSysModuleInfo->ImageName);
   }
   pSysModuleInfo++;
}
}


该功能是通过遍历PsLoadedModuleList实现的,所以要隐藏的话,最简单的方法还是断链~~
更高级的方法比如抹DriveObject,抹PE信息等等,以后再玩~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值