您的位置:首页 > 其它

[Win32] ToolHelp API 和 进程API 详解

2015-08-17 15:38 183 查看
本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:/article/9672536.html

一:ToolHelp API

MSDN:https://msdn.microsoft.com/en-us/library/ms686832(VS.85).aspx

关于Toolhelp API,MSDN是这样解释的:

The functions provided by the tool help library make it easier for you to obtain information about currently executing applications. These functions are designed to streamline the creation of tools.

提供的工具帮助库的功能方便您可以获取有关当前正在执行的应用程序的信息。这些功能旨在简化创建工具。

其实,ToolHelp API就是用于枚举进程,枚举模块,获取进程和模块信息的工具API。

1。CreateToolhelp32Snapshot

HANDLE WINAPI CreateToolhelp32Snapshot(
_In_ DWORD dwFlags,
_In_ DWORD th32ProcessID
);

创建进程快照

dwFlags:

TH32CS_INHERIT:指示是可继承的快照句柄。

TH32CS_SNAPALL:包括所有进程和线程,再加上堆的 th32ProcessID 中指定的进程和模块。等效于指定 TH32CS_SNAPHEAPLIST,TH32CS_SNAPMODULE,TH32CS_SNAPPROCESS 和 TH32CS_SNAPTHREAD 的值组合使用或运算 (|)。

TH32CS_SNAPHEAPLIST:包括所有堆的快照中的 th32ProcessID 中指定的进程。若要枚举的堆,请参阅 Heap32ListFirst。

TH32CS_SNAPMODULE:包括所有模块的快照中的 th32ProcessID 中指定的进程。若要枚举模块,请参阅 Module32First。如果函数失败与 ERROR_BAD_LENGTH,重试功能,直到成功为止。

64 位 Windows: 在 32 位进程中使用此标志,而在 64 位进程中使用 64 位的进程。若要包括 64 位进程从 th32ProcessID 中指定的进程的 32 位模块,请使用 TH32CS_SNAPMODULE32 标志。

TH32CS_SNAPMODULE32:包括所有的 32 位模块的 th32ProcessID 在快照时从 64 位进程调用中指定的进程。此标志可以结合 TH32CS_SNAPMODULE 或 TH32CS_SNAPALL。如果函数失败与 ERROR_BAD_LENGTH,重试功能,直到成功为止。

TH32CS_SNAPPROCESS:包含在快照中系统中的所有进程。若要枚举的进程,请参阅 Process32First。

TH32CS_SNAPTHREAD:包含在快照中系统中的所有线程。若要枚举的线程,请参阅 Thread32First。若要标识属于特定进程的线程,线程进行枚举时比较 THREADENTRY32 结构的 th32OwnerProcessID 成员及其进程标识符。

th32ProcessID:

要包含在快照中的进程的进程标识符。这个参数可以是零,以表明当前进程。当指定的 TH32CS_SNAPHEAPLIST、 TH32CS_SNAPMODULE、 TH32CS_SNAPMODULE32 或 TH32CS_SNAPALL 的值,将使用此参数。否则为它将被忽略,所有流程都包含在快照中。

如果指定的进程是空闲进程或 CSRSS 之一处理,此函数将失败和最后的错误代码是 ERROR_ACCESS_DENIED,因为它们的访问限制阻止用户级代码打开它们。

如果指定的进程是一个 64 位的过程和调用方是 32 位的进程,此函数将失败和最后的错误代码是 ERROR_PARTIAL_COPY (299)。

返回值:如果此函数成功,它返回一个打开句柄到指定快照。如果函数失败,则返回 INVALID_HANDLE_VALUE

简单解释:如果你想枚举进程,dwFlags用TH32CS_SNAPPROCESS,此时th32ProcessID会被忽略,要枚举模块,用TH32CS_SNAPMODULE(64位程序枚举32位进程的模块时用TH32CS_SNAPMODULE32),枚举模块时th32ProcessID表示进程ID

2。Process32First

BOOL WINAPI Process32First(
_In_    HANDLE           hSnapshot,
_Inout_ LPPROCESSENTRY32 lppe
);

获取进程快照中的第一个进程信息

hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。

lppe:指向 PROCESSENTRY32 结构的指针。它包含进程信息,如名称的可执行文件、 进程标识符和父进程的进程标识符。

返回值:如果进程列表中的第一项已被复制到缓冲区将返回 TRUE,失败返回FALSE。如果没有进程存在或快照不包含进程信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。

调用应用程序必须设置 PROCESSENTRY32 的 dwSize 成员的大小,以字节为单位的结构。

3。PROCESSENTRY32 结构

typedef struct tagPROCESSENTRY32 {
DWORD     dwSize;
DWORD     cntUsage;
DWORD     th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD     th32ModuleID;
DWORD     cntThreads;
DWORD     th32ParentProcessID;
LONG      pcPriClassBase;
DWORD     dwFlags;
TCHAR     szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;

dwSize:结构体大小

cntUsage:此成员不再使用,并始终设置为零。

th32ProcessID:进程ID

th32DefaultHeapID:此成员不再使用,并始终设置为零

th32ModuleID:此成员不再使用,并始终设置为零

cntThreads:此成员不再使用,并始终设置为零。

th32ParentProcessID:创建此进程 (其父进程) 的进程标识符。

pcPriClassBase:此过程创建的任何线程的基本优先级。

dwFlags:此成员不再使用,并始终设置为零。

szExeFile:进程的可执行文件的名称。若要检索的可执行文件的完整路径,调用 Module32First 函数,并检查返回的 MODULEENTRY32 结构的 szExePath 成员。然而,如果调用进程是一个 32 位进程,则必须调用 QueryFullProcessImageName 函数来检索 64 位进程的可执行文件的完整路径。

4。Process32Next
BOOL WINAPI Process32Next(
_In_  HANDLE           hSnapshot,
_Out_ LPPROCESSENTRY32 lppe
);

获取下一个进程信息

hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。

lppe:指向 PROCESSENTRY32 结构的指针。它包含进程信息,如名称的可执行文件、 进程标识符和父进程的进程标识符。(见上面)

返回值:如果进程列表中的下一项已被复制到缓冲区将返回 TRUE,失败返回FALSE。如果没有进程存在或快照不包含进程信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。

5。Module32First
BOOL WINAPI Module32First(
_In_    HANDLE          hSnapshot,
_Inout_ LPMODULEENTRY32 lpme
);


获取第一个模块信息

hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。

lppe:指向 MODULEENTRY32 结构的指针。它包含模块信息。

返回值:成功TRUE,失败FALSE。如果没有模块存在或快照不包含模块信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。

调用应用程序必须设置 MODULEENTRY32 的 dwSize 成员的大小,以字节为单位的结构。若要检索有关与指定的进程相关联的其他模块的信息,请使用 Module32Next 函数。

6。MODULEENTRY32 结构

typedef struct tagMODULEENTRY32 {
DWORD   dwSize;
DWORD   th32ModuleID;
DWORD   th32ProcessID;
DWORD   GlblcntUsage;
DWORD   ProccntUsage;
BYTE    *modBaseAddr;
DWORD   modBaseSize;
HMODULE hModule;
TCHAR   szModule[MAX_MODULE_NAME32 + 1];
TCHAR   szExePath[MAX_PATH];
} MODULEENTRY32, *PMODULEENTRY32;

dwSize:结构以字节为单位的大小。在调用 Module32First 函数之前, 将此成员设为 sizeof(MODULEENTRY32)。如果你不做初始化 dwSize,Module32First会失败

th32ModuleID:此成员不再使用,并始终设置为1。

th32ProcessID:其模块所在的进程的标识符。

GlblcntUsage:模块装载计数,通常没有意义而被设置为 0xFFFF。

ProccntUsage:模块装载计数 (和GlblcntUsage 相同),通常没有意义而被设置为 0xFFFF。

modBaseAddr:所拥有的进程的上下文中模块的基址。

modBaseSize:该模块,以字节为单位的大小。

hModule:所拥有的进程的上下文中的模块的句柄。

szModule:模块名称。

szExePath“模块路径。

备注:ModBaseAddr 和 hModule 成员是进程的仅由 th32ProcessID 指定的上下文中有效。

7。Module32Next

BOOL WINAPI Module32Next(
_In_  HANDLE          hSnapshot,
_Out_ LPMODULEENTRY32 lpme
);

获取下一个模块信息

hSnapshot:调用 CreateToolhelp32Snapshot 函数返回的快照句柄。

lppe:指向 MODULEENTRY32 结构的指针。它包含模块信息。

返回值:成功TRUE,失败FALSE。如果没有模块存在或快照不包含模块信息时GetLastError将返回 ERROR_NO_MORE_FILES 错误值。

例子:枚举所有进程并将添加到ListView
//在窗口的回调函数的WM_CREATE中:
listview1 = CreateWindowEx(WS_EX_STATICEDGE, TEXT("SysListView32"), NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL, 5, 5,475, 500, hwnd, (HMENU)1, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);//创建ListView
ListView_SetExtendedListViewStyle(listview1, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_INFOTIP);//设置listview扩展风格
SendMessage(listview1, WM_SETFONT, (WPARAM)GetStockObject(17), 0);

SetWindowTheme(listview1, L"Explorer", NULL);

//内存清零
RtlZeroMemory(&list1, sizeof(LVCOLUMN));

//创建列
list1.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;//掩码
list1.fmt = LVCFMT_LEFT;//左对齐
list1.cx = 160;//列宽
list1.pszText = TEXT("进程名称");
SendMessage(listview1, LVM_INSERTCOLUMN, 0, (LPARAM)&list1);//创建列

list1.pszText = TEXT("PID");
list1.cx = 60;
SendMessage(listview1, LVM_INSERTCOLUMN, 1, (LPARAM)&list1);

list1.pszText = TEXT("父进程PID");
SendMessage(listview1, LVM_INSERTCOLUMN, 2, (LPARAM)&list1);

list1.pszText = TEXT("进程映像路径");
list1.cx = 300;
SendMessage(listview1, LVM_INSERTCOLUMN, 3, (LPARAM)&list1);

listprocess();

listprocess函数:

void listprocess(){
PROCESSENTRY32 pro32;
TCHAR id[10];
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)return;
RtlZeroMemory(&pro32, sizeof(PROCESSENTRY32));
pro32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnapshot, &pro32);
do{
//创建项目
RtlZeroMemory(&item1, sizeof(LVITEM));
item1.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_INDENT;
item1.pszText = pro32.szExeFile;
item1.iItem = 0;//项目号
item1.iImage = 0;//图片号
item1.iIndent = 0;
SendMessage(listview1, LVM_INSERTITEM, 0, (LPARAM)&item1);

//创建子项
item1.mask = LVIF_TEXT | LVIF_IMAGE;
item1.iImage = -1;

_itot(pro32.th32ProcessID, id, 10);
item1.iItem = 0;
item1.iSubItem = 1;
item1.pszText = id;
SendMessage(listview1, LVM_SETITEM, 0, (LPARAM)&item1);
_itot(pro32.th32ParentProcessID, id, 10);
item1.iItem = 0;
item1.iSubItem = 2;
item1.pszText = id;
SendMessage(listview1, LVM_SETITEM, 0, (LPARAM)&item1);
item1.iItem = 0;
item1.iSubItem = 3;
item1.pszText = TEXT("");
SendMessage(listview1, LVM_SETITEM, 0, (LPARAM)&item1);
} while (Process32Next(hSnapshot, &pro32));
CloseHandle(hSnapshot);
}

效果图:



关于获取路径的问题,将会在下下篇中说。

二: 进程API

其实进程API这个名词来源于MSDN中的“Process and Thread Functions”(进程和线程函数),其中进程那一部分我把它叫做“进程API”。大家千万别把进程API和PsAPI搞混了,PsAPI不是Process API,而应该是“Process Status API”(进程状态API)
1。OpenProcess
HANDLE WINAPI OpenProcess(
_In_ DWORD dwDesiredAccess,
_In_ BOOL  bInheritHandle,
_In_ DWORD dwProcessId
);

打开一个进程,获取进程句柄。
我们知道,Windows中有一种东西叫做“内核对象”,前面我们讲过的“图标”、“位图”、“窗口”、“设备上下文”等等都是“内核对象”,而应用程序是不能访问在内存中的内核对象的,换句话说,应用程序不能得到一个内核对象的指针,应用程序只能通过获取内核对象的“句柄”来间接访问内核对象,OpenProcess这个API,就是获取进程内核对象的句柄的。
dwDesiredAccess:对进程对象的访问权限。此参数可以是一个或多个进程的访问权限。(注意:如果调用方已启用 SeDebugPrivilege 特权,请求的访问权限的授予与安全描述符的内容无关。)
进程访问权限:
PROCESS_ALL_ACCESS:所有访问权限。

PROCESS_CREATE_PROCESS:创建进程。

PROCESS_CREATE_THREAD:创建线程。

PROCESS_DUP_HANDLE:需要重复使用 DuplicateHandle 的句柄。

PROCESS_QUERY_INFORMATION:检索过程中,如其令牌的某些信息需退出代码和优先级类。

PROCESS_QUERY_LIMITED_INFORMATION:需要检索某些有关进程的信息 (见 GetExitCodeProcess,GetPriorityClass,IsProcessInJob,QueryFullProcessImageName)。一个句柄,有 PROCESS_QUERY_INFORMATION 的访问权限,则会自动授予 PROCESS_QUERY_LIMITED_INFORMATION。Windows Server 2003 和 Windows XP: 不支持此访问权限。

PROCESS_SET_INFORMATION:所需设置的过程中,如其优先级类的某些信息 (见 SetPriorityClass)。

PROCESS_SET_QUOTA:设置内存限制,使用 SetProcessWorkingSetSize。

PROCESS_SUSPEND_RESUME:需暂停或恢复进程。

PROCESS_TERMINATE:终止进程。

PROCESS_VM_OPERATION:需要对进程的地址空间执行操作 (VirtualProtectEx和WriteProcessMemory)。

PROCESS_VM_READ:读取内存,使用ReadProcessMemory。

PROCESS_VM_WRITE:写入内存,使用WriteProcessMemory。

SYNCHRONIZE:需等待该进程终止,使用等待函数。

DELETE:所需删除的对象。

READ_CONTROL:读取该对象的 SACL 中不包括信息安全描述符中的信息所需。要读取或写入对象的 SACL,您必须要求 ACCESS_SYSTEM_SECURITY 的访问权限。更多的信息,请参阅 SACL 访问权限。

SYNCHRONIZE :的权利要用于同步的对象。这使线程等待,直到该对象处于终止状态。

WRITE_DAC:需要修改该对象的安全描述符中的 DACL。

WRITE_OWNER:所需更改的所有者在对象的安全描述符。


bInheritHandle:如果此值为 TRUE,此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。
dwProcessId:要打开进程的PID

返回值:如果此函数成功,返回值是指定进程的打开句柄。如果函数失败,返回值为 NULL。若要获取扩展的错误信息,请调用GetLastError
2。TerminateProcess
BOOL WINAPI TerminateProcess(
_In_ HANDLE hProcess,
_In_ UINT   uExitCode
);

强行终止一个进程。
The TerminateProcess function is used to unconditionally cause a process to exit. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.

This function stops execution of all threads within the process and requests cancellation of all pending I/O. The terminated process cannot exit until all pending I/O has been completed or canceled. When a process terminates, its kernel object is not destroyed until all processes that have open handles to the process have released those handles.

TerminateProcess is asynchronous; it initiates termination and returns immediately. If you need to be sure the process has terminated, call the WaitForSingleObject function with a handle to the process.

A process cannot prevent itself from being terminated.

从上面的MSDN说明中,我们可以得出以下结论:

1.TerminateProcess是无条件终止进程,进程终止之前不会收到通知

2.要终止的进程打开的内核对象直到所有打开该内核对象的进程释放此内核对象之前不会释放

3.TerminateProcess是异步的,他不会等待进程完全退出,但可以使用WaitForSingleObject等待进程句柄被系统释放

hProcess:要终止的进程的句柄。该句柄必须具有 PROCESS_TERMINATE 的访问权限。

uExitCode:退出码,置0即可

返回值:成功返回非0,失败返回0

例子:关闭一个进程

BOOL KillProcessAndWait(DWORD dwProcessId){
HANDLE hProcess=OpenProcess(PROCESS_TERMINATE|SYNCHRONIZE, FALSE, dwProcessId);
if (hProcess == NULL)return FALSE;
if (TerminateProcess(hProcess, 0)){
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
return TRUE;
}
else{
CloseHandle(hProcess);
return FALSE;
}
}


3。还有很多很多的进程API,这里不再一一介绍了,对了在注册表的那两节(进入)里,用到了一个IsWow64Process函数,这个函数也是进程API,判断一个32位进程是否处于WOW64环境。

更多进程API,请参见MSDN“进程和线程函数”:https://msdn.microsoft.com/en-us/library/windows/desktop/ms684847(v=vs.85).aspx



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: