SysInternals 的 WinObj 可以列出所有设备对象。
我想知道它如何列出设备。
有没有我们可以阅读的开源代码?(或代码片段)
我应该知道的最重要的功能是什么?
SysInternals 的 WinObj 可以列出所有设备对象。
我想知道它如何列出设备。
有没有我们可以阅读的开源代码?(或代码片段)
我应该知道的最重要的功能是什么?
WinObj 使用 NT 系统调用NtOpenDirectoryObject
和NtQueryDirectoryObject
. 不需要驱动程序或内核代码。您不会看到导入,因为这些 NT 函数是通过LoadLibrary
/加载的GetProcAddress
。
您不必枚举整个对象命名空间。如果您对使用 调用的设备对象感兴趣,请NtOpenDirectoryObject
调用"\Device"
返回NtQueryDirectoryObject
的句柄。
本机 NT API 提供了允许用户模式程序浏览名称空间并查询位于该名称空间的对象的状态的例程,但这些接口未记录在案。
我试过查看 WinObj 的导入表(dumpbin /imports winobj.exe
),但没有明显的嫌疑 :-(
根据user1575778的回答,您可以使用NtOpenDirectoryObject
and NtQueryDirectoryObject
(在用户模式下分别与ZwOpenDirectoryObject
和ZwQueryDirectoryObject
相同)列出对象管理器命名空间内的对象。
看看NT Objects aka ntobjxobjmgr.hpp
,特别是在类(或)中。它提供了相同的功能,很好地封装在一个 C++ 类中。整个实用程序在自由许可下是开源的(由于 WTL 使用而获得双重许可:MIT 和 MS-PL),因此只要您遵守许可条款,您就可以随意重用零碎的东西。NtObjMgr::Directory
DirectoryT
但这里有一个简单的 C++ 代码示例,适合您的用例:
#include <Windows.h>
#include <tchar.h>
#include <cstdio>
#include <winternl.h>
NTSTATUS (NTAPI* NtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
NTSTATUS (NTAPI* NtQueryDirectoryObject)(HANDLE, PVOID, ULONG, BOOLEAN, BOOLEAN, PULONG, PULONG);
VOID (NTAPI* RtlInitUnicodeString_)(PUNICODE_STRING, PCWSTR);
NTSTATUS (NTAPI* NtClose_)(HANDLE);
#define DIRECTORY_QUERY (0x0001)
#define DIRECTORY_TRAVERSE (0x0002)
typedef struct _OBJECT_DIRECTORY_INFORMATION {
UNICODE_STRING Name;
UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION;
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth
#endif // STATUS_SUCCESS
#ifndef STATUS_MORE_ENTRIES
#define STATUS_MORE_ENTRIES ((NTSTATUS)0x00000105L)
#endif // STATUS_MORE_ENTRIES
#ifndef STATUS_NO_MORE_ENTRIES
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
#endif // STATUS_NO_MORE_ENTRIES
int PrintDevices()
{
NTSTATUS ntStatus;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING objname;
HANDLE hDeviceDir = NULL;
RtlInitUnicodeString_(&objname, L"\\Device");
InitializeObjectAttributes(&oa, &objname, 0, NULL, NULL);
ntStatus = NtOpenDirectoryObject(&hDeviceDir, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, &oa);
if(NT_SUCCESS(ntStatus))
{
size_t const bufSize = 0x10000;
BYTE buf[bufSize] = {0};
ULONG start = 0, idx = 0, bytes;
BOOLEAN restart = TRUE;
for(;;)
{
ntStatus = NtQueryDirectoryObject(hDeviceDir, PBYTE(buf), bufSize, FALSE, restart, &idx, &bytes);
if(NT_SUCCESS(ntStatus))
{
POBJECT_DIRECTORY_INFORMATION const pdilist = reinterpret_cast<POBJECT_DIRECTORY_INFORMATION>(PBYTE(buf));
for(ULONG i = 0; i < idx - start; i++)
{
if(0 == wcsncmp(pdilist[i].TypeName.Buffer, L"Device", pdilist[i].TypeName.Length / sizeof(WCHAR)))
{
_tprintf(_T("%s\n"), pdilist[i].Name.Buffer);
}
}
}
if(STATUS_MORE_ENTRIES == ntStatus)
{
start = idx;
restart = FALSE;
continue;
}
if((STATUS_SUCCESS == ntStatus) || (STATUS_NO_MORE_ENTRIES == ntStatus))
{
break;
}
}
(void)NtClose_(hDeviceDir);
return 0;
}
_tprintf(_T("Failed NtOpenDirectoryObject with 0x%08X"), ntStatus);
return 1;
}
int _tmain(int /*argc*/, _TCHAR** /*argv*/)
{
HMODULE hNtDll = ::GetModuleHandle(_T("ntdll.dll"));
*(FARPROC*)&NtOpenDirectoryObject = ::GetProcAddress(hNtDll, "NtOpenDirectoryObject");
*(FARPROC*)&NtQueryDirectoryObject = ::GetProcAddress(hNtDll, "NtQueryDirectoryObject");
*(FARPROC*)&RtlInitUnicodeString_ = ::GetProcAddress(hNtDll, "RtlInitUnicodeString");
*(FARPROC*)&NtClose_ = ::GetProcAddress(hNtDll, "NtClose");
if (!NtOpenDirectoryObject || !NtQueryDirectoryObject || !RtlInitUnicodeString_ || !NtClose_)
{
_tprintf(_T("Failed to retrieve ntdll.dll function pointers\n"));
return 1;
}
return PrintDevices();
}
一些评论:这不会深入研究子目录,它不会列出除符号链接之外的任何类型Device
,并且不会解析符号链接(如果有)。对于任何这些功能,请查看上述实用程序的源代码并根据需要进行调整。winternl.h
应该在任何最近的 Windows SDK 中可用。
函数RtlInitUnicodeString_
和NtClose_
有一个尾随下划线以避免与这些本地 API 函数发生冲突,这些函数在 中声明winternl.h
,但使用__declspec(dllimport)
.
披露:我是 ntobjx 的作者。
您可以使用 NtOpenDirectoryObject 和 NtQueryDirectoryObject 枚举给定目录中的对象列表。
要获取对象名称空间的详细信息,您必须使用 Windows NT Undocumented API。WinObj 也使用它,因为这里描述了 WinOBj 如何获得所有结果..对于那些说我们需要驱动程序来执行此操作的人,请阅读给定页面上的这些行。
“一个明显的方法是使用驱动程序——在内核模式下,一切都可以访问——因此客户端应用程序可以通过与自己的驱动程序通信来获取所需的信息。然而,WinObj 不使用驱动程序(这是它能够执行的原因之一没有管理员权限,虽然有管理员权限,但它显示所有对象而不是部分结果)。”
您可以从 SetupDiCreateDeviceInfoList 开始,并使用其他相关函数来枚举所有设备。这东西用起来很痛苦。