如何枚举进程的句柄?

29

有没有办法在Windows中枚举给定PID的进程,并获取其所有已打开的句柄(锁定的文件等)列表?

编辑:语言无所谓。如果是.NET,我会很高兴;如果是WinApi(C),也不会有问题。如果是其他语言,我想我可以重写它 :-)

3个回答

29
我进行了深入的谷歌搜索,并找到了这篇文章。 该文章提供了一个链接,可以下载源代码
我尝试了NtSystemInfoTest.cpp中的方法(下载的源代码),效果非常好。
void ListHandles( DWORD processID, LPCTSTR lpFilter )

这段代码具有以下的声明:

// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
// For companies(Austin,TX): If you would like to get my resume, send an email.
//
// The source is free, but if you want to use it, mention my name and e-mail address
//
//////////////////////////////////////////////////////////////////////////////////////
//

希望这能帮到你。


太棒了!这正是我所需要的(而且无法通过谷歌搜索到)。谢谢。 :-) - nothrow
1
不幸的是,尽管这样可以工作,但仍需要调用NtQuerySystemInformation,它会返回所有句柄,然后需要迭代并按所需PID进行过滤。据我所知,没有办法向操作系统请求仅针对一个进程的句柄。 - Bartek Banachewicz
当句柄是管道时,这种方法无效。NtQueryObject将永远挂起。 - vadim_hr

8
如果您只需要一个工具,Sysinternals 的命令行 'Handle' 工具可以实现此功能。但是,如果您正在寻找代码解决方案,则无法帮助您。

Handle 的 GUI 对应物 Process Explorer 也可以实现此功能。在 FindFind Handle or DLL 菜单中可打开此对话框。搜索路径仅在省略驱动器号时有效(正如 Handle 的文档中所述)。 - legends2k

7

这里有一个示例,使用DDK中的ZwQueryProcessInformation。DDK现在已被称为“WDK”,可以在MSDN上获得。如果您没有MSDN,显然您也可以从这里获取。

我没有尝试过,只是通过谷歌搜索回答了你的问题。

#include "ntdll.h"
#include <stdlib.h>
#include <stdio.h>
#include "ntddk.h"

#define DUPLICATE_SAME_ATTRIBUTES 0x00000004

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

BOOL EnablePrivilege(PCSTR name)
{
TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);

HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);

AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
BOOL rv = GetLastError() == ERROR_SUCCESS;

CloseHandle(hToken);
return rv;
}

int main(int argc, char *argv[])
{
if (argc == 1) return 0;

ULONG pid = strtoul(argv[1], 0, 0);

EnablePrivilege(SE_DEBUG_NAME);

HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);

ULONG n = 0x1000;
PULONG p = new ULONG[n];

while (NT::ZwQuerySystemInformation(NT::SystemHandleInformation, p, n * sizeof *p, 0)
== STATUS_INFO_LENGTH_MISMATCH)

delete [] p, p = new ULONG[n *= 2];

NT::PSYSTEM_HANDLE_INFORMATION h = NT::PSYSTEM_HANDLE_INFORMATION(p + 1);

for (ULONG i = 0; i < *p; i++) {

if (h[i].ProcessId == pid) {
HANDLE hObject;

if (NT::ZwDuplicateObject(hProcess, HANDLE(h[i].Handle), NtCurrentProcess(), &hObject,
0, 0, DUPLICATE_SAME_ATTRIBUTES)
!= STATUS_SUCCESS) continue;

NT::OBJECT_BASIC_INFORMATION obi;

NT::ZwQueryObject(hObject, NT::ObjectBasicInformation, &obi, sizeof obi, &n);

printf("%p %04hx %6lx %2x %3lx %3ld %4ld ",
h[i].Object, h[i].Handle, h[i].GrantedAccess,
int(h[i].Flags), obi.Attributes,
obi.HandleCount - 1, obi.PointerCount - 2);

n = obi.TypeInformationLength + 2;

NT::POBJECT_TYPE_INFORMATION oti = NT::POBJECT_TYPE_INFORMATION(new CHAR[n]);

NT::ZwQueryObject(hObject, NT::ObjectTypeInformation, oti, n, &n);

printf("%-14.*ws ", oti[0].Name.Length / 2, oti[0].Name.Buffer);

n = obi.NameInformationLength == 0
? MAX_PATH * sizeof (WCHAR) : obi.NameInformationLength;

NT::POBJECT_NAME_INFORMATION oni = NT::POBJECT_NAME_INFORMATION(new CHAR[n]);

NTSTATUS rv = NT::ZwQueryObject(hObject, NT::ObjectNameInformation, oni, n, &n);
if (NT_SUCCESS(rv))
printf("%.*ws", oni[0].Name.Length / 2, oni[0].Name.Buffer);

printf("\n");

CloseHandle(hObject);
}
}
delete [] p;

CloseHandle(hProcess);

return 0;
}

好的,我也谷歌了一下,但是无法下载Windows Vista版本的DDK :-( 所以我认为可能有其他解决方案(sysinternals的ProcessExplorer根本不会链接ntdll) - nothrow
1
ProcessExplorer不会静态链接到ntdll,但它会在运行时加载。您可以使用depends工具找到它。 - Shay Erlichmen
如果你正在使用SYNC_READ打开一个管道并对其进行ZwQueryObject(..ObjectNameInformation..)操作,如何解决该方案卡住的问题? - mrduclaw
1
@nothrow - 每个进程都会隐式链接到ntdll,它是Windows加载器的一部分。这在Mark Russinovich的《Windows Internals》一书中有所讨论,详见第3章“映像加载器”。 - antiduh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接