进程枚举的四种方式

进程枚举是将系统中的所有进程显示出来,就像Windows中的任务管理器一样,可以看到系统中运行的所有进程。

 

进程枚举的四种方法:

  1. 通过系统快照进行枚举

  2. 通过psapi.dll中的函数进行枚举

  3. 通过WtsApi32.dll中的函数进行枚举

  4. 通过ntdll.dll中的函数进行枚举

第一种、通过系统快照进行枚举

1、CreateToolhelp32Snapshot()

CreateToolhelp32Snapshot可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。

HANDLE WINAPI CreateToolhelp32Snapshot(
    DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
    DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取                             当前进程快照时可以设为0

);

参数:

  • dwFlags指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个。
TH32CS_INHERIT(0x80000000)- 声明快照句柄是可继承的。
TH32CS_SNAPALL- 在快照中包含系统中所有的进程和线程。
TH32CS_SNAPHEAPLIST(0x00000001)- 在快照中包含在th32ProcessID中指定的进程的所有的堆。
TH32CS_SNAPMODULE(0x00000008)- 在快照中包含在th32ProcessID中指定的进程的所有的模块。
TH32CS_SNAPPROCESS(0x00000002)- 在快照中包含系统中所有的进程。
TH32CS_SNAPTHREAD(0x00000004)- 在快照中包含系统中所有的线程。

H32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)

  • th32ProcessID指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
  • 返回值

调用成功,返回快照的句柄,调用失败,返回INVALID_HANDLE_VALUE 。

2、Process32First()

process32First是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用process32First函数来获得

BOOL WINAPI Process32First(

    HANDLE hSnapshot,//_in

    LPPROCESSENTRY32 lppe//_out

);
  • hSnapshot是快照句柄,
  • lppe是一个指向PROCESSENTRY32结构的指针,进程信息将会被返回到这个结构中。

       PROCESSENTRY32 结构如下:

typedef struct tagPROCESSENTRY32 {
    DWORD dwSize; // 结构大小;
    DWORD cntUsage; // 此进程的引用计数;
    DWORD th32ProcessID; // 进程ID;
    DWORD th32DefaultHeapID; // 进程默认堆ID;
    DWORD th32ModuleID; // 进程模块ID;
    DWORD cntThreads; // 此进程开启的线程计数;
    DWORD th32ParentProcessID;// 父进程ID;
    LONG pcPriClassBase; // 线程优先权;
    DWORD dwFlags; // 保留;
    WCHAR szExeFile[MAX_PATH]; // 进程全名;
} PROCESSENTRY32;

3、Process32Next()

Process32Next(
    Handle hsnapShot,
    LPPROCESSENTRY32 lppe
)

Process32Next是获取快照中的下一个进程的函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用process32First函数来获得

  • hSnapshot是快照句柄,
  • lppe是一个指向PROCESSENTRY32结构的指针,进程信息将会被返回到这个结构中。

       PROCESSENTRY32 结构如下


typedef struct tagPROCESSENTRY32 {
    DWORD dwSize; // 结构大小;
    DWORD cntUsage; // 此进程的引用计数;
    DWORD th32ProcessID; // 进程ID;
    DWORD th32DefaultHeapID; // 进程默认堆ID;
    DWORD th32ModuleID; // 进程模块ID;
    DWORD cntThreads; // 此进程开启的线程计数;
    DWORD th32ParentProcessID;// 父进程ID;
    LONG pcPriClassBase; // 线程优先权;
    DWORD dwFlags; // 保留;
    WCHAR szExeFile[MAX_PATH]; // 进程全名;
} PROCESSENTRY32;
 

实例:

#include <Windows.h>
#include <TlHelp32.h>
#include  <stdio.h>

int main()
{
    PROCESSENTRY32 processEntry = {0};

    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        return -1;
    }
    processEntry.dwSize = sizeof(PROCESSENTRY32);
    BOOL bRet = Process32First(hProcessSnap, &processEntry);
    while (bRet)
    {

    printf("ProcessID:%d (%s)\n", processEntry.th32ProcessID, processEntry.szExeFile);
    bRet = Process32Next(hProcessSnap, &processEntry);

    }
    CloseHandle(hProcessSnap);
    system("pause");
    return 0;
}

 

第二种、通过psapi.dll中的函数进行枚举

1、EnumProcesses()

EnumProcesses检索进程中的每一个进程标识符.EnumProcesses函数主要功能得到一系列过程采用EnumProcesses功能。为每个过程、主要功能通过工艺标识符调用PrintModules功能。

BOOL WINAPI EnumProcesses(

    DWORD * pProcessIds,
    DWORD CB,
    DWORD * pBytesReturned

);

 

  • pProcessIds接收进程标识符的数组.
  • cb数组的大小.
  • pBytesReturned数组返回的字节数
  • 成功返回非零数,失败返回零,可以使用函数 GetLastError获取错误信息.

2、OpenProcess()

OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄

HANDLE OpenProcess(
    DWORD dwDesiredAccess, //渴望得到的访问权限(标志)
    BOOL bInheritHandle, // 是否继承句柄
    DWORD dwProcessId// 进程标示符
);
  • dwDesiredAccess [1]  :获取的权限,可分为以下几种
PROCESS_ALL_ACCESS:获取所有权限
PROCESS_CREATE_PROCESS:创建进程
PROCESS_CREATE_THREAD:创建线程
PROCESS_DUP_HANDLE:使用DuplicateHandle()函数复制一个新句柄
PROCESS_QUERY_INFORMATION:获取进程的令牌、退出码和优先级等信息
PROCESS_QUERY_LIMITED_INFORMATION:获取进程特定的某个信息
PROCESS_SET_INFORMATION:设置进程的某种信息
PROCESS_SET_QUOTA:使用SetProcessWorkingSetSize函数设置内存限制
PROCESS_SUSPEND_RESUME:暂停或者恢复一个进程
PROCESS_TERMINATE:使用Terminate函数终止进程
PROCESS_VM_OPERATION:在进程的地址空间执行操作
PROCESS_VM_READ:使用ReadProcessMemory函数在进程中读取内存
PROCESS_VM_WRITE:使用WriteProcessMemory函数在进程中写入内存
SYNCHRONIZE:使用wait函数等待进程终止

 

  • bInheritHandle:TRUE或者FALSE
  • dwProcessId:pid
  • 返回值如成功,返回值为指定进程的句柄如失败,返回值为空,可调用GetLastError获得错误代码。

 

3、EnumProcessModles()


获得指定进程中所有模块的句柄。

BOOL WINAPI EnumProcessModules(
    in  HANDLE hProcess,
    out  HMODULE *lphModule,
    in   DWORD cb,
    out  LPDWORD lpcbNeeded
);

 

  • hProcess [传入]指定进程的句柄。
  • lphModule [传出]用来存放所有模块句柄的数组。
  • cb [传入]lphModule参数所传入的数组的大小,单位是字节。
  • lpcbNeeded [传出]要把所有模块的句柄存放进lphModule参数所传入的数组中,所需要的字节数。
  • 返回

如果函数执行成功,则返回值为非零。果函数执行失败,则返回值为零。可以调用 GetLastError函数来获得更多的错误信息。

 

4、GetModuleFileNameEx()

获取指定模块的文件名称

DWORD GetModuleFileNameEx(
     HANDLE hProcess,    //进程的句柄
    HMODULE hModule,    // 获取模块的句柄 
    LPTSTR lpFilename,  //接收的地址空间 
    DWORD nSize         // 接收空间的大小

);
  •  hProcess,    //进程的句柄
  •  hModule,    // 获取模块的句柄 
  •  lpFilename,  //接收的地址空间 
  •  nSize         // 接收空间的大小
  • 返回值:如果获取成功,则返回一个接收地址空间的大小如果不成功返回值为0,可以使用GetLastError()函数获取返回的具体信息。 

例程:此例程总共分为以下几个步骤:

第一步、通过EnunProcesses()函数获取进程中系统中所有的进程,将获取到的进程标识符(进程ID)放入到一个数组中(此函数的第一个参数)中,

第二步、OpenProcess函数通过获取到的进程标识符打开进程,如果打开成功,则返回进程的句柄。

第三步、EnumProcessModles函数通过OpenProcess函数返回的进程句柄枚举进程中所有的模块,并将获取的到的所有模块句柄储存到一个数组中(此函数的第二个参数)

第四步、使用GetModuleFileNameEx函数通过进程和模块的句柄获取当前进程中所有模块的名称。并将其存储到一个地址空间中(此函数的第三个参数)

#include <Windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <stdlib.h>

int main()
{
	//提升进程权限
	//打开令牌
	HANDLE hToken;//创建令牌句柄
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS | TOKEN_QUERY, &hToken))//打开令牌句柄,设置令牌权限
	{
		printf("OpenProcessToken Failed.");
		return -1;
	}
	TOKEN_PRIVILEGES tkp;
	LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);//查看令牌权限
	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
	if (GetLastError() != ERROR_SUCCESS)
	{
		printf("AdjustTokenPrivileges Failed.");
		return -1;
	}
	
	
	//枚举进程
	DWORD ProcessId[1024];
	DWORD cbNeeded;
	DWORD processcount;

	if (!EnumProcesses(ProcessId,sizeof(ProcessId),&cbNeeded))//枚举所有进程的ID
	{
		printf("Failed.");
		return -1;
	}
	processcount = cbNeeded / sizeof(DWORD);

	printf("当前一共有%d个进程\n\n",processcount);
	HMODULE hMods[1024];
	for (DWORD i = 0; i < processcount; i++)
	{
		HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,ProcessId[i]);//通过ID,打开所有的进程
		
		if (hProcess)
		{
			//printf("\nProcessID:%d 打开成功\n",ProcessId[i]);
			if (EnumProcessModules(hProcess,hMods,sizeof(hMods),&cbNeeded))//通过EnumProcessModules获取进程模块
			{
				for (int  j = 0; j < (cbNeeded/sizeof(HMODULE)); j++)
				{
					TCHAR szModName[MAX_PATH];
					if (GetModuleFileNameEx(hProcess,hMods[j],szModName,sizeof(szModName)/sizeof(TCHAR)))
					{
						printf("\t%d %s (0x%08)\n",j,szModName,hMods[j]);
					}
				}

			}
		}
		else
		{
			printf("\nProcessID:%d 打开失败\n", ProcessId[i]);
		}
		//printf("ProcessId: %d\n",ProcessId[i]);

	}
	system("pause");
	return 0;
}

 

 

第三种、通过WtsApi32.dll中的函数进行枚举

此方式中,是将电脑当做一台终端服务器来枚举系统中的进程,使用的函数小编没有找到资料,以自己的理解对这个方式进行描述。

例程:

第一步、使用WTSOpenServer函数通过计算机名称获取终端服务器的句柄。

第二步、通过WTSEnumerateProcesses函数枚举系统的进程,并将获取的经常信息放入到一个进程结构体重(此函数的第四个参数)

第三步、关闭终端服务器的句柄

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <WtsApi32.h>

#pragma comment(lib,"WtsApi32.lib")

int main()
{
	DWORD i;
	char* szServerName = "DESKTOP-IIOTQRA";//定义自己的计算机名
	HANDLE hWtsServer = WTSOpenServer(szServerName);//通过计算机名打开一个特殊终端服务器的句柄,并且返回句柄
	PWTS_PROCESS_INFO pWtspi;//定义一个结构体,保存终端进程的信息
	DWORD dwCount;
	if (!WTSEnumerateProcesses(hWtsServer,0,1,&pWtspi,&dwCount))//枚举系统进程
	{
		printf("Error!");
		return -1;
	}
	for ( i = 0; i < dwCount; i++)
	{
		printf("ProcessID : %d (%s)\n",pWtspi[i].ProcessId,pWtspi[i].pProcessName);
	}
	WTSCloseServer(hWtsServer);//关闭特殊终端服务器的句柄
	system("pause");
	return 0;
}

第四种、通过ntdll.dll中的函数进行枚举

#include <iostream>
#include <Windows.h>
#include <NTSecAPI.h>
using namespace std;
typedef DWORD(WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);//使用指针函数,调用用函数GetProcAddress
#define SystemProcessesAndThreadsInformation 5 //定义GetProcAddress函数要访问的的形式
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;
int main()
{
	HMODULE hNtDLL = GetModuleHandle("ntdll.dll");//动态获取ntdll.dll,如果获取成功返回一个句柄。
	if (!hNtDLL)
	{
		cout << "Error: -1" << endl;
		return -1;
	}
	ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDLL, "ZwQuerySystemInformation");//
	//动态获取GetProcAddress,将此函数模块的首地址返回给指针函数ZwQuerySystemInformation
	ULONG cbBuffer = 0x10000;
	LPVOID pBuffer = NULL;
	pBuffer = malloc(cbBuffer);//分配一个内存地址空间
	if (pBuffer == NULL)
	{
		cout << "Error: -2" << endl;
		return -2;
	}
	ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pBuffer, cbBuffer, NULL);//通过指针函数将系统中的进程进行枚举
	PSYSTEM_PROCESS_INFORMATION pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;//将分配内存地址中的内容转换成进程信息结构的结构体信息
	for (;;)
	{
		printf("ProcessID: %d (%ls)\n", pInfo->ProcessId, pInfo->ProcessName.Buffer);
		if (pInfo->NextEntryDelta == 0)
			break;
		pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);//指向下一个将进程信息结构元素
	}
	free(pBuffer);//释放分配的内存地址
	system("pause");
	return 0;
}

注意:此文章只作为小编的笔记,有部分资料参照网上,若有侵权,请联系删除。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值