函数ZwQuerySystemInformation小结

来源:互联网 发布:人才招聘源码 码农网 编辑:程序博客网 时间:2024/06/05 21:49
函数存在于NTDLL.DLL动态链接库中。NTDLL.DLL负责ring3与ring0之间的通信。当使用子系统方式进行系统调用的时候,ntdll.dll和SSDT会配合使用。
关于ZwQuerySystemInformation这个函数可以用来查询进程信息、内核信息、硬件信息(例如CPU数目)、句柄信息、时间信息等54个系统信息。
该函数的原型是
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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. );  


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

一、用户模式下的ZwQuerySystemInformation
在用户模式下必须用LoadLibrary与GetProcAddress来获取该函数地址。
代码如下,
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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");  


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

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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;   

 循环程序如下:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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.   }  

二、内核模式下的ZwQuerySystemInformation
内核模式下的ZwQuerySystemInformation的地址的获取没有应用层那么麻烦。直接声明一下该函数即可。
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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);  
注意这里的开头使用了一个NTKERNELAPI,这个宏我不知道是干啥的,就到几个群里问了一下,得到了答案,它是在winddk.h这个头文件中声明的,如下:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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  

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

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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. }  

这是一个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个元素的模块信息数组了。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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;  
一个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信息等等,以后再玩~

0 0
原创粉丝点击