枚举进程的几种方法

来源:互联网 发布:google拼音输入法 mac 编辑:程序博客网 时间:2024/05/16 07:51

原地址:http://blog.sina.com.cn/s/blog_97312deb01017loy.html

1、利用ToolHelp API

首先创建一个系统快照,然后通过对系统快照的访问完成进程的枚举。

获取系统快照使用CreateToolhelp32Snapshot()函数

函数原型声明如下:

HANDLE WINAPICreateToolhelp32Snapshot(
               DWORDdwFlags,
               DWORDth32ProcessID
);

dwFlags设置为TH32CS_SNAPPROCESS用于获取进程快照。函数调用成功后会返回一个快照的句柄,便可以使用Process32First()Process32Next()函数进行枚举了。

函数原型声明如下:

BOOL WINAPIProcess32First(
           HANDLEhSnapshot,
           LPPROCESSENTRY32lppe
);

BOOL WINAPIProcess32Next( 
           HANDLEhSnapshot,
           LPPROCESSENTRY32lppe
);


下面是相关代码:

#include<windows.h>
#include <tlhelp32.h>
#include <stdio.h>

voiduseToolHelp()
{
    HANDLE procSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(procSnap == INVALID_HANDLE_VALUE)
    {
        printf("CreateToolhelp32Snapshot failed,%d ",GetLastError());
        return;
    }
   //
   PROCESSENTRY32 procEntry = { 0 };
   procEntry.dwSize = sizeof(PROCESSENTRY32);
   BOOL bRet = Process32First(procSnap,&procEntry);
   while(bRet)
   {
      wprintf(L"PID: %d (%s)",procEntry.th32ProcessID, procEntry.szExeFile); 

      //在此可以做想要处理的相关工作

      //...
      bRet = Process32Next(procSnap,&procEntry);

   }


   CloseHandle(procSnap);

}

void main()
{
    useToolHelp();
    getchar();
}


用此方法可以在进程ID和进程名称间进行转换,即可以通过进程名称获得进程ID,也可以通过进程ID获取进程名称。

注:该方法对32位机器有效,但是对64位机器可不一定有效哟!


2、通过psapi.dll提供的函数

通过psapi.dll提供的EnumProcesses()EnumProcessModules()函数实现

函数原型声明如下:

BOOLEnumProcesses(
           DWORD*pProcessIds,
           DWORD cb,
           DWORD*pBytesReturned
);

BOOLEnumProcessModules(
           HANDLEhProcess,
           HMODULE*lphModule,
           DWORDcb, 
           LPDWORDlpcbNeeded
);

相关代码如下:(参考MSDN

#include<windows.h>
#include <stdio.h>
#include <tchar.h>
#include "psapi.h"
#pragma comment(lib,"psapi.lib")


void PrintProcessNameAndID(DWORD processID)
{
     TCHAR szProcessName[MAX_PATH] = _T("");
      HANDLE hProcess =OpenProcess(PROCESS_ALL_ACCESS , FALSE,processID);     


     //Process name.

     if(NULL!= hProcess)
     {
         HMODULEhMod;
         DWORDcbNeeded;
         if(EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))         
         {              

               GetModuleBaseName(hProcess,hMod, szProcessName,

                                                   sizeof(szProcessName)/ sizeof(TCHAR));                        
         }
     }
     wprintf(_T("PID: %d (%s) "), processID,szProcessName);

     CloseHandle(hProcess);
}

 

void main( )
{
     DWORD aProcesses[1024], cbNeeded, cProcesses;
     unsigned int i;
     if( !EnumProcesses(aProcesses,sizeof(aProcesses), &cbNeeded) )
           return;


     cProcesses = cbNeeded / sizeof(DWORD);

     for(i= 0; i < cProcesses; i++)
         PrintProcessNameAndID(aProcesses[i]);


     getchar();

}

 

此方法由于需要进行OpenProcess操作,所以需要一定的权限,当权限不够时,有些进程将不能被打开。

下面给出提升权限的相关代码:

voidRaisePrivilege()
{
   HANDLE hToken;
   TOKEN_PRIVILEGES tp;
   tp.PrivilegeCount = 1;
   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

   //
  if( OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS,&hToken) )

  {
     if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid) )
     {
           AdjustTokenPrivileges(hToken,FALSE, &tp, NULL, NULL, 0);
     }
  }


  if(hToken)

       CloseHandle(hToken);
}

注:该方法适用于32bit64bit机器。

 

3、通过ntdll.dll提供的Native API

使用Native APIZwQuerySystemInformation()函数,指定SystemProcessesAndThreadsInformation标记获取系统调用枚举进程。由于该函数没有被导出,所以首先定义要使用到的结构和常量

// 定义调用函数指针
typedef DWORD(WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

typedef struct _SYSTEM_PROCESS_INFORMATION
{
  DWORD NextEntryDelta;
  DWORD ThreadCount;
  DWORD Reserved1[6];
  FILETIME ftCreateTime;
  FILETIME ftUserTime;
  FILETIME ftKernelTime;
  UNICODE_STRING ProcessName;
  DWORD BasePriority;
  DWORD ProcessId;
  DWORD InheritedFromProcessId;
  DWORD HandleCount;
  DWORD Reserved2[2];
  DWORD VmCounters;
  DWORD dCommitCharge;
  PVOID ThreadInfos[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

#defineSystemProcessesAndThreadsInformation 5

然后动态加载ntdll.dll,获得函数的地址。便可以进行进程的枚举

相关代码如下:

#include<windows.h>
#include <ntsecapi.h>
#include <stdio.h>

typedef DWORD(WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

 

typedef struct _SYSTEM_PROCESS_INFORMATION
{
  DWORD NextEntryDelta;
  DWORD ThreadCount;
  DWORD Reserved1[6];
  FILETIME ftCreateTime;
  FILETIME ftUserTime;
  FILETIME ftKernelTime;
  UNICODE_STRING ProcessName;
  DWORD BasePriority;
  DWORD ProcessId;
  DWORD InheritedFromProcessId;
  DWORD HandleCount;
  DWORD Reserved2[2];
  DWORD VmCounters;
  DWORD dCommitCharge;
  PVOID ThreadInfos[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

#defineSystemProcessesAndThreadsInformation 5

void main()
{
    HMODULE hNtDll =GetModuleHandle(_T("ntdll.dll"));
    if(!hNtDll)
         return;


    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation =   

                                          (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, 

                                                                                                            "ZwQuerySystemInformation");
    ULONGcbBuffer = 0x10000;
    LPVOIDpBuffer = NULL;
    pBuffer= malloc(cbBuffer);
    if(pBuffer== NULL)
                 return;


   ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,pBuffer,cbBuffer,NULL);

   PSYSTEM_PROCESS_INFORMATIONpInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
   for(;;)
   {
        wprintf(L"PID:%d (%ls) ",pInfo->ProcessId,pInfo->ProcessName.Buffer);
        if(pInfo->NextEntryDelta== 0)
                   break;
        pInfo= (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
   }


   free(pBuffer);

   getchar();
}

代码在VS2005 release + XP SP2调试通过

 

4、通过进程打开的句柄来枚举进程

     同样使用ZwQuerySystemInformation()函数,指定SystemHandleInformation标记,取得系统调用

相关代码如下:

#include<windows.h>
#include <ntsecapi.h>
#include <ntstatus.h>
#include <stdio.h>

typedef NTSTATUS(WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

 

typedef struct_SYSTEM_HANDLE_INFORMATION 
{
  ULONG ProcessId;
  UCHAR ObjectTypeNumber;
  UCHAR Flags;
  USHORT Handle;
  PVOID Object;
  ACCESS_MASK GrantedAccess;
}SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

 

typedef struct_SYSTEM_HANDLE_INFORMATION_EX 
{
  ULONG NumberOfHandles;
  SYSTEM_HANDLE_INFORMATION Information[1];
}SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;

#defineSystemHandleInformation 0x10  // 16

 

void main()
{
   HMODULE hNtDll = LoadLibrary(L"ntdll.dll");
   if(!hNtDll)
         return;


   ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation =

                                         (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, 
                                                                                                             "ZwQuerySystemInformation");

  ULONGcbBuffer = 0x4000;
  LPVOIDpBuffer = NULL;
  NTSTATUSsts;


 do

{
   pBuffer= malloc(cbBuffer);
   if(pBuffer== NULL)
                   return;
   memset(pBuffer,0,cbBuffer);


  sts = ZwQuerySystemInformation(SystemHandleInformation, pBuffer,cbBuffer, NULL);

  if(sts== STATUS_INFO_LENGTH_MISMATCH) 
  {
      free(pBuffer);

      pBuffer= NULL;
      cbBuffer = cbBuffer * 2; //
初始分配的空间不足,加倍。
  }
}while(s ==STATUS_INFO_LENGTH_MISMATCH);

 

PSYSTEM_HANDLE_INFORMATION_EXpInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;
ULONG OldPID = 0;


for(DWORD i = 0; i < pInfo->NumberOfHandles; i++)

{
   if(OldPID!= pInfo->Information[i].ProcessId)
   {
      OldPID= pInfo->Information[i].ProcessId;
      wprintf(L"PID:%d ",OldPID);
   }
}

 

 free(pBuffer);
 pBuffer = NULL;
 FreeLibrary(hNtDll);
 hNtDll = NULL;

 getchar();
}

另外,在进行进程隐藏工作的时候,此处的句柄是一件容易被忽略的地方,因此需要注意隐藏由程序打开的相关句柄,由于系统中句柄数量经常变换,所以没有什么必要修改其中的NumberOfHandles域,因为如果修改此处的值,则需要不停对句柄的变化进行维护,开销比较大。

在用户态下的进程枚举已经变得不可靠,因为一个内核级的Rootkit很容易就能够更改这些函数的返回结果,因此进程的可靠枚举应在内核态中实现,可以通过编写驱动来实现。

 

0 0