Enumerating DeviceObjects from user mode

Most people that works in software development or consultancy, are familiar with SysInternals Suite (or at last they have heard about them). In this entry, we are going to focus on one of those tools: WinObj. The WinObj tool allows us, among other things the enumerate all the items in the Windows Object Manager: a object directory that is maintained by the Windows kernel and that contains information about all the device objects created by drivers and by the kernel itself. Some of those devices are also related to a kind of object called “Symbolic Link” that is typically used to allow access to those objects from user mode applications. On the following picture, you can see a capture of WinObj application when it is executed on a typical Windows 8.1 box:

WinObjAs you can see, WinObj shows the different objects which Object Manager maintains in the system. You can also see how the objects are organized on directories (much like if they were files) and how the link is related to a given object.

Sometimes you need to deal with device objects (for example if you would like to know the relationships between physical disks and the partitions that are sit on them). The way to do this is to make a query against Object Manager but the first question that arises is How could I do this from user mode? Then is when some undocumented NTDLL.DLL services go to the rescue!

The services I am talking about are the following:

NtOpenDirectoryObject
NtQueryDirectoryObject
NtOpenSymbolicLinkObject
NtQuerySymbolicLinkObject

In order to call them, you will need to load NTDLL.DLL by means of:

HMODULE _hModule = LoadLibrary(_T("ntdll.dll"));

The next thing is to get a pointer the functions of our interest:

NtOpenDirectoryObject = (NTOPENDIRECTORYOBJECT)GetProcAddress(_hModule, "NtOpenDirectoryObject");
NtQueryDirectoryObject = (NTQUERYDIRECTORYOBJECT)GetProcAddress(_hModule, "NtQueryDirectoryObject");
NtOpenSymbolicLinkObject = (NTOPENSYMBOLICLINKOBJECT)GetProcAddress(_hModule, "NtOpenSymbolicLinkObject");
NtQuerySymbolicLinkObject = (NTQUERYSYMBOLICLINKOBJECT)GetProcAddress(_hModule, "NtQuerySymbolicLinkObject");

The types like NTOPENDIRECTORYOBJECT are just pointers to functions that we have defined to be able to call the functions. Based on some undocumented information, we know that the pointers to these functions should look like:

typedef NTSTATUS(WINAPI *NTOPENDIRECTORYOBJECT)(
	_Out_  PHANDLE DirectoryHandle,
	_In_   ACCESS_MASK DesiredAccess,
	_In_   POBJECT_ATTRIBUTES ObjectAttributes
	);
 
typedef NTSTATUS(WINAPI *NTQUERYDIRECTORYOBJECT)(
	_In_       HANDLE DirectoryHandle,
	_Out_opt_  PVOID Buffer,
	_In_       ULONG Length,
	_In_       BOOLEAN ReturnSingleEntry,
	_In_       BOOLEAN RestartScan,
	_Inout_    PULONG Context,
	_Out_opt_  PULONG ReturnLength
	);
 
typedef NTSTATUS (WINAPI *NTOPENSYMBOLICLINKOBJECT)(
	_Out_  PHANDLE LinkHandle,
	_In_   ACCESS_MASK DesiredAccess,
	_In_   POBJECT_ATTRIBUTES ObjectAttributes
	);
 
typedef NTSTATUS (WINAPI *NTQUERYSYMBOLICLINKOBJECT)(
	_In_       HANDLE LinkHandle,
	_Inout_    PUNICODE_STRING LinkTarget,
	_Out_opt_  PULONG ReturnedLength
	);

And the final step to enumerate the device objects is just to open the directory and query every single item that it contains. In this example, we are using CLI/C++ just for the shake of convenience:

OBJECT_DIRECTORY_INFORMATION *pObjectDirectoryInformation = NULL;
int bufferSize = 1024 * sizeof(OBJECT_DIRECTORY_INFORMATION);
ULONG context, returnLength;
 
List<String^>^ deviceNames = gcnew List<String^>();
Dictionary<String^, String^>^ deviceObjects = gcnew Dictionary<String^, String^>();
 
PUCHAR pBuffer = (PUCHAR)LocalAlloc(LPTR, bufferSize);
 
HANDLE hDirectory = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
UNICODE_STRING usDirectoryName = { 0 };
RtlInitUnicodeString(&usDirectoryName, DIRECTORY_NAME);
InitializeObjectAttributes(&objectAttributes, &usDirectoryName, OBJ_CASE_INSENSITIVENULLNULL);
 
NTSTATUS status = NtOpenDirectoryObject(&hDirectory, DIRECTORY_QUERY, &objectAttributes);
if (status == STATUS_SUCCESS)
{
	BOOL restart = TRUE;
	do
	{
		status = NtQueryDirectoryObject(hDirectory, pBuffer, bufferSize, FALSE, restart, &context, &returnLength);
		restart = FALSE;
		pObjectDirectoryInformation = (OBJECT_DIRECTORY_INFORMATION*)pBuffer;
		for (int i = 0; pObjectDirectoryInformation[i].Name.Buffer; i++)
		{
			String^ deviceName = gcnew System::String(pObjectDirectoryInformation[i].Name.Buffer);
			deviceObjects->Add(deviceName, gcnew System::String(pObjectDirectoryInformation[i].TypeName.Buffer));
		}
	} while (status == STATUS_MORE_ENTRIES);
}
else
{
	Debug::Print("NtOpenDirectoryObject failed with status = {0:X}\n", status);
}

I hope this information could result useful for you.

Advertisements

One thought on “Enumerating DeviceObjects from user mode

  1. Pingback: Research | Illuminated Prism

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s