What process context are you running in? Unless you are guaranteed to be operating in a system process, the ZwClose calls remain unsafe.
Note that even if you unset the protected handle bit, a malicious caller could still (re)set the protected handle bit in between the calls to ZwSetInformationObject and ZwClose. This is because, relative to the handle table, separate calls to ZwSetInformationObject and ZwClose do not have atomic transaction semantics.
If you are not guaranteed to be operating in a system process, then requesting OBJ_KERNEL_HANDLE instead of the same attributes may be safer.
Also, you *must* specify a POBJECT_TYPE for ObReferenceObjectByHandle. Otherwise, you might get back a handle that no longer refers to the expected object type (perhaps now a handle to an event object, or something else). This is a pool corruption bug at best and a privilege escalation bug to kernel mode at worst.
If you are expecting a file object, the right thing to do would be to pass *IoFileObjectType.
-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, October 02, 2008 1:55 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Problems with ObReferenceObject and ObDerefenceObject
Thank you all for the replies. Here is some clarification and a copy of the code I ended up using:
I have a strong marketing requirement to make the TDI filter run (as much as possible) without requiring a reboot after installation. To do this, I do the following:
- install my TDI filter driver. Once installed the driver intercepts all new activity, with some limitations, e.g., event handlers on extant address objects are not intercepted, TDI direct send operations are missed, etc.
- enumerate all handles in the system using ZwQuerySystemInformation(SystemHandleInformation)
- scan through the table of handles and try to determine if any of the handles refers to a TDI address/connection object. It is OK if a handle I got in the list gets closed and another one replaces it: that object does not exist anymore. I just want to do what I can to discover the TDI objects, and missing some will not be a problem: not that my filter driver is already running.
- to determine if a handle refers to a TDI object I need to get a safe reference to the underlying object, so that: i can read its fields, and can be sure that there will be no race with my filter in destroying state for that object.
- for each discovered TDI address object I check if my filter knows about it, and if not, I create some state. As part of the state creation I have a safe way to redirect the event handlers registered for this object, so that I can start seeing new connect/disconnect events for server-side sockets.
- I deref each refed object and move on to the next one in the list
Here is the code I ended up using after reading the comments to this thread. It is based on my earlier approach with refinements borrowed from Jackob Bohm’s comment. Any comments/suggestions are more than welcome. This code seems to be working so far, but I am about to put it through some more rigorous testing.
Thanks,
–aydan
// Attempts to obtain a safe reference to the specified object:
static PFILE_OBJECT ReferenceObject(PSYSTEM_HANDLE_TABLE_ENTRY_INFO pHandleEntry, OUT PHANDLE pProcessHandle, OUT PVOID * ppProcess, OUT PHANDLE pObjectHandle)
{
NTSTATUS status = STATUS_SUCCESS;
CLIENT_ID clientID = {0, 0};
OBJECT_ATTRIBUTES objectAttributes;
HANDLE objectHandleToUse = NULL;
PFILE_OBJECT result = NULL;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
ASSERT(pHandleEntry != NULL);
ASSERT(pProcessHandle != NULL);
ASSERT(ppProcess != NULL);
ASSERT(pObjectHandle != NULL);
// clean all out parameters
*pProcessHandle = NULL;
*pObjectHandle = NULL;
*ppProcess = NULL;
// ignore any handles owned by pid 0. Those seem to cause access violations when
// attempting to open the process
if (pHandleEntry->UniqueProcessId == 0){
return NULL;
}
clientID.UniqueProcess = (HANDLE)pHandleEntry->UniqueProcessId;
objectHandleToUse = (HANDLE)pHandleEntry->HandleValue;
// check if system is the owner
if (clientID.UniqueProcess != PsGetCurrentProcessId()){
// initialize the object attributes
InitializeObjectAttributes (&objectAttributes, 0, 0, 0 , 0);
// get a handle to the process that owns the handle
status = ZwOpenProcess(pProcessHandle, PROCESS_DUP_HANDLE, &objectAttributes, &clientID);
if (!NT_SUCCESS(status)){
BsLog_WARN(“Failed to open process with PID=%p. Status=%08x”, clientID.UniqueProcess, status);
goto out;
}
// reference the owner process
status = ObReferenceObjectByHandle(*pProcessHandle, FILE_ANY_ACCESS, NULL, UserMode, ppProcess, NULL);
if (!NT_SUCCESS(status)){
BsLog_WARN(“Failed to reference process object with PID=%p. Status=%08x”, clientID.UniqueProcess, status);
goto out;
}
// duplicate the handle of the object we are trying to reference
status = ZwDuplicateObject(*pProcessHandle,
(HANDLE)pHandleEntry->HandleValue,
ZwCurrentProcess(),
pObjectHandle,
0, 0, DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(status)){
BsLog_WARN(“Failed to obtain handle to object. Status=%08x”, status);
goto out;
}
objectHandleToUse = *pObjectHandle;
}
// now that we have a handle to the object, attempt to reference the pointer
status = ObReferenceObjectByHandle(objectHandleToUse, FILE_ANY_ACCESS, *IoFileObjectType, UserMode, &result, NULL);
if (!NT_SUCCESS(status)){
BsLog_WARN(“Failed to obtain object from handle. Status=%08x”, status);
result = NULL;
goto out;
}
out:
if (!NT_SUCCESS(status)){
// close the object handle
if (*pObjectHandle != NULL){
// make the handle closeable
OBJECT_HANDLE_ATTRIBUTE_INFORMATION info = {TRUE, FALSE};
ZwSetInformationObject(*pObjectHandle, ObjectHandleInformation, &info, sizeof(info));
// close the handle
ZwClose(*pObjectHandle);
*pObjectHandle = NULL;
}
// dereference the process object
if (*ppProcess != NULL){
ObDereferenceObject(*ppProcess);
*ppProcess = NULL;
}
// close the process handle
if (*pProcessHandle != NULL){
ZwClose(*pProcessHandle);
*pProcessHandle = NULL;
}
}
return result;
}
// Dereferences pFile (if previously referenced)
static void DereferenceObject(PFILE_OBJECT pFile, HANDLE processHandle, PVOID pProcess, HANDLE objectHandle)
{
// dereference the object
if (pFile != NULL){
ObDereferenceObject(pFile);
}
// close the object handle
if (objectHandle != NULL){
// make the handle closeable
OBJECT_HANDLE_ATTRIBUTE_INFORMATION info = {TRUE, FALSE};
//BsLog_DEBUG(“Closing object handle”);
ZwSetInformationObject(objectHandle, ObjectHandleInformation, &info, sizeof(info));
ZwClose(objectHandle);
}
// dereference the process object
if (pProcess != NULL){
ObDereferenceObject(pProcess);
}
// close the process handle
if (processHandle != NULL){
ZwClose(processHandle);
}
}
NTDEV is sponsored by OSR
For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars
To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer