Problems with ObReferenceObject and ObDerefenceObject

> Example: usermode app passes you event handle in ioctl buffer.

Then you call KeSetEvent on that event. Who will validate the handle?

Wonderful point, Pavel - indeed, I should have thought of some well-known example that passes UM handle to ObReferenceObjectByHandle(), and, at this point, it would become obvious to me that passing invalid handle is not a problem at all…

Anton Bassov

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);
}
}

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.

  • S

-----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

> I have a strong marketing requirement to make the TDI filter run (as much as possible)

without requiring a reboot after installation.

This is the root of all problems.

Indeed, sometimes you have to deal with brain-damaged requirements that don’t make any sense from the technical standpoint, because it improves your product marketability - at this point no one cares that it may make your product. unstable. Example - company X wants to make a TDI driver unloadable, because, in their opinion, a client may want to be able to update a driver without rebooting the system. They don’t care that TDI filter is a “legacy”, rather than PnP, driver, i.e. is not the kind of driver that can be safely detached the stack. The funniest thing is that, AFAIK, in actuality, a client requested this “fancy feature”
(as well as few other ones) to be removed, because they were much more concerned about the system stability…

Anton Bassov

I do not know anything about TDI filters. Do they have a requirement
that they be inserted when the stack is building up or can they insert
themselves into an existing stack.
I’ve had a similar need (no reboot!) in the past for storage (blocks)
filtering.
I created two drivers. One interceptor that does sit in the stack (and
requires a reboot to install) and a buddy driver (manual load|unload
many times!!) with all the real functionality. Interceptor in thin and
simple and talks privatly to buddy driver.

Hope it helps
Cheers
Harish

-----Original Message-----
From: xxxxx@gmail.com [mailto:xxxxx@gmail.com]
Sent: Thursday, October 02, 2008 10:55 AM
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

Ken,

Thank you for the comments.

What process context are you running in? Unless you are guaranteed to be
operating in a system process, the ZwClose calls remain unsafe.

This code is invoked by the driver’s DriverEntry routine. I assume that one runs in the context of System. Is this correct? I will use OBJ_KERNEL_HANDLE anyway.

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.

I agree. But I think the risk here is not that high: I am not closing the original handle, but the new handle I got from ZwDuplicateObject. This handle is only present in the system process and exists for a short period of time. This does not mean that it is impossible for another driver to go and change this handle between the calls to ZwSetInformationObject and ZwClose, but I do not think any reasonable driver is going to do that. Our customers are expected to have restricted environments and my assumption is that there will be no malicious drivers running in them. But your point still stands.

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.

I call ObReferenceObjectByHandle twice in that function: once for the owner process object (with NULL object type) and one for the actual file object (with *IoFileObjectType). The process object is opaque for me: I only keep it around to make sure the process does not disappear. I guess I can specify an object type, but I do not know what object type to use for a handle that refers to a process (*PsProcessType?).

Thanks,
–aydan

> I do not know anything about TDI filters. Do they have a requirement that they be inserted

when the stack is building up or can they insert themselves into an existing stack.

The latter…

This is why then can get stopped manually via SCM, which is really unsaf - just consider what happens if some other filter gets attached on top of yours. With PnP driver it is much easier - it just would not allow anything like that. The only reason why PnP driver may get detached is because its stack is being unwound - it cannot happen upon some arbitrary request…

I’ve had a similar need (no reboot!) in the past for storage (blocks) filtering. I created two drivers.
One interceptor that does sit in the stack (and requires a reboot to install) and a buddy driver
(manual load|unload many times!!) with all the real functionality.

This approach is very well known. However, the company in question was deadly certain that it did not want to have any additional components. They were even doing things that objectively belong in the UM (for example, FP calucations), right in a driver, because they believed it would make the product easier to install and maintain. In general, their driver was, in all respects, extremely unreasonable, from the technical standpoint - they were much more interested in marketing, rather than in technical issues…

Anton Bassov

Yeah, the FILE_ANY_ACCESS there threw me off. (Which is, BTW, not the correct access mask constant to use here.) You do not need to call ObReferenceObjectByHandle on the process handle for the reason that you had listed. Simply opening a handle to it (via ZwOpenProcess) will keep the underlying object alive (obviously, it is not a guard against process termination, but incrementing the handle count for an object is conceptually incrementing the reference count as well, such that the HANDLE represents a reference to the object). Even if the process is terminated while you have a handle open to it, the underlying data structures remain allocated, although certain system services will return a (graceful) failure status if they operate on a terminating process. Having an explicit handle or pointer reference of your own does not change this behavior, as long as you have one or the other.

I would, in general, always use OBJ_KERNEL_HANDLE unless you have a very specific reason not to (i.e. if you were returning a HANDLE to user mode). Note that opening HANDLEs on behalf of the user in kernel mode and returning them is typically discouraged in most circumstances, as it is usually preferable to open a handle from user mode and share it to kernel mode (if you are trying to, say, share an event across an app and a driver as a signaling mechanism). I don’t see you doing this here, however.

Re-reading that code again, yes, you don’t need to worry about a bad driver having reprotected your handle, as a bad driver is a “game over” scenario, anyway. If you did not use DUPLICATE_SAME_ATTRIBUTES, then the close protection flag would not be copied to the new handle (but instead defaulted to unset), and you would not need to call ZwSetInformationObject(…ObjectHandleInformation…) all over to ensure that you would not be trying to close a protected handle.

N.B. I still think that this is going to be a support can of worms for you in the field, if for nothing else than the other, differing aspects of what you are trying to do, but…

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Thursday, October 02, 2008 2:41 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

Ken,

Thank you for the comments.

What process context are you running in? Unless you are guaranteed to be
operating in a system process, the ZwClose calls remain unsafe.

This code is invoked by the driver’s DriverEntry routine. I assume that one runs in the context of System. Is this correct? I will use OBJ_KERNEL_HANDLE anyway.

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.

I agree. But I think the risk here is not that high: I am not closing the original handle, but the new handle I got from ZwDuplicateObject. This handle is only present in the system process and exists for a short period of time. This does not mean that it is impossible for another driver to go and change this handle between the calls to ZwSetInformationObject and ZwClose, but I do not think any reasonable driver is going to do that. Our customers are expected to have restricted environments and my assumption is that there will be no malicious drivers running in them. But your point still stands.

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.

I call ObReferenceObjectByHandle twice in that function: once for the owner process object (with NULL object type) and one for the actual file object (with *IoFileObjectType). The process object is opaque for me: I only keep it around to make sure the process does not disappear. I guess I can specify an object type, but I do not know what object type to use for a handle that refers to a process (*PsProcessType?).

Thanks,
–aydan


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

I wanted to post a follow-up on my experience running the code I posted earlier. It turns out that the behavior of ObReferenceObjectByHandle depends on whether the driver verifier is enabled. When the driver verifier is not enabled, passing an invalid handle to ObReferenceObjectByHandle returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH (note: in both cases I use UserMode for the access mode parameter). These observations match the documentation in the DDK.

However, things are different if the driver verifier is enabled. Passing an invalid handle or attempting to perform and invalid typecast will not return the expected status code (regardless of the AccessMode). Instead, the verifier will trigger a BSOD (code c4, error: c3).

I am not sure why the verifier team chose this behavior. To me this seems like a false positive, and will be relevant only if the callers of ObReferenceObjectByHandle disregard the return value.

There are several options to deal with this problem:

  1. Disable the verifier:

Not preferable. The verifier has helped me find a lot of problems.

  1. Use MmGetSystemRoutineAddress to obtain the real address of ObReferenceObjectByHandle. Note than any regular calls to ObReferenceObjectByHandle use the driver verifier’s version of this function when the verifier is enabled. I read somewhere that this technique may not work on Vista and above. This is not a problem for me, since our highest target platform in 2003-x64. I’ve tested on windows 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.

–aydan

This is not a ‘false positive’. It is an error to call that function with
an invalid handle. Verifier checks it and will break into the debugger when
that rule is violated. Passing random addresses to kernel functions is a
very bad idea. It can produce side effects that may not be seen for some
time and can occur in places where there would appear to be no relation to
the causing error. This technique of letting the OS catch my errors is a
user mode way of thinking that MUST be avoided in kernel mode. In kernel
mode you are a ‘part’ of the OS with all the powers of the OS and nothing
you do should allow techniques of this sort.

wrote in message news:xxxxx@ntdev…
>I wanted to post a follow-up on my experience running the code I posted
>earlier. It turns out that the behavior of ObReferenceObjectByHandle
>depends on whether the driver verifier is enabled. When the driver verifier
>is not enabled, passing an invalid handle to ObReferenceObjectByHandle
>returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform
>an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH
>(note: in both cases I use UserMode for the access mode parameter). These
>observations match the documentation in the DDK.
>
> However, things are different if the driver verifier is enabled. Passing
> an invalid handle or attempting to perform and invalid typecast will not
> return the expected status code (regardless of the AccessMode). Instead,
> the verifier will trigger a BSOD (code c4, error: c3).
>
> I am not sure why the verifier team chose this behavior. To me this seems
> like a false positive, and will be relevant only if the callers of
> ObReferenceObjectByHandle disregard the return value.
>
> There are several options to deal with this problem:
>
> 1. Disable the verifier:
>
> Not preferable. The verifier has helped me find a lot of problems.
>
> 2. Use MmGetSystemRoutineAddress to obtain the real address of
> ObReferenceObjectByHandle. Note than any regular calls to
> ObReferenceObjectByHandle use the driver verifier’s version of this
> function when the verifier is enabled. I read somewhere that this
> technique may not work on Vista and above. This is not a problem for me,
> since our highest target platform in 2003-x64. I’ve tested on windows
> 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.
>
> --aydan
>

No, this is incorrect.

ObReferenceObjectByHandle is *how you validate handles given to you by a user.*, if it is called with AccessMode == UserMode, with with a valid set of DesiredAccess / ObjectType arguments.

It’s a bug to pass an untrusted handle with AccessMode == KernelMode, or without a valid DesiredAccess / ObjectType.

It’s absolutely not a bug to use ObReferenceObjectByHandle to attempt to convert an untrusted handle acquired by a user into an object type pointer of a desired object type category, with a set of required access rights needing to have been associated with that handle.

Verifier bugchecking with AccessMode == UserMode and the DesiredAccess / ObjectType arguments being valid is debatable. It’ll catch any user mode program that supplies a bad handle to you, but the bug here would be in the user mode app, and not the driver. (Obviously, not a bug you want to have in your app - however - if a driver uses ObReferenceObjectByHandle correctly, it is not susceptible to attack via that vector from a malicious user mode program, simply because of it using ObReferenceObjectByHandle. There could of course be other driver bugs, but this is the correct and proper use of ObReferenceObjectByHandle)

Consider that the WDK samples that accept handles validate them using ObReferenceObjectByHandle, and so do all of the system services supplied by the kernel. This is the way things are intended to work.

To the OP: If you have a service that communicates handles to the driver and it’s sending bad handles, you should fix that, as it’s a bug in the app. However, the driver still needs to handle the case of a bad handle being sent to guard against a malicious app (and it sounds like you’re doing that, which is a good thing).

If you have a custom test suite that is attempting to verify that your driver handles this case, and verifier is causing it to bugcheck, then that’s one reason why this verifier behavior is questionable in my mind, as you’re kind of forced to run tests without verifier engaged for that case.

  • S

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of David Craig
Sent: Tuesday, October 07, 2008 12:41 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

This is not a ‘false positive’. It is an error to call that function with
an invalid handle. Verifier checks it and will break into the debugger when
that rule is violated. Passing random addresses to kernel functions is a
very bad idea. It can produce side effects that may not be seen for some
time and can occur in places where there would appear to be no relation to
the causing error. This technique of letting the OS catch my errors is a
user mode way of thinking that MUST be avoided in kernel mode. In kernel
mode you are a ‘part’ of the OS with all the powers of the OS and nothing
you do should allow techniques of this sort.

wrote in message news:xxxxx@ntdev…
>I wanted to post a follow-up on my experience running the code I posted
>earlier. It turns out that the behavior of ObReferenceObjectByHandle
>depends on whether the driver verifier is enabled. When the driver verifier
>is not enabled, passing an invalid handle to ObReferenceObjectByHandle
>returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform
>an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH
>(note: in both cases I use UserMode for the access mode parameter). These
>observations match the documentation in the DDK.
>
> However, things are different if the driver verifier is enabled. Passing
> an invalid handle or attempting to perform and invalid typecast will not
> return the expected status code (regardless of the AccessMode). Instead,
> the verifier will trigger a BSOD (code c4, error: c3).
>
> I am not sure why the verifier team chose this behavior. To me this seems
> like a false positive, and will be relevant only if the callers of
> ObReferenceObjectByHandle disregard the return value.
>
> There are several options to deal with this problem:
>
> 1. Disable the verifier:
>
> Not preferable. The verifier has helped me find a lot of problems.
>
> 2. Use MmGetSystemRoutineAddress to obtain the real address of
> ObReferenceObjectByHandle. Note than any regular calls to
> ObReferenceObjectByHandle use the driver verifier’s version of this
> function when the verifier is enabled. I read somewhere that this
> technique may not work on Vista and above. This is not a problem for me,
> since our highest target platform in 2003-x64. I’ve tested on windows
> 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.
>
> --aydan
>


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

It seems perfectly reasonable to me to pass a ‘value’ into
ObReferenceObjectByHandle() and expect the OS to validate it and only return
an error if the ‘value’ is not a handle, not a handle to the correct type,
or cannot be acquired with the requested access rights. This is exactly
what the function is for. Handles are by their very nature a type of mildly
untrusted identifier with (mostly) safe semantics provided by
ObReferenceObjectByHandle(). This is exactly the case where verifier
throwing a nutty is over the top. The function returns a status. The
contract is not in violation if I pass in 0xDEADBEEF for a handle and it
returns STATUS_INVALID_HANDLE (or STATUS_YOU_SHOULD_EAT_VEGETARIAN, or any
other status).

Now everything you said in reference to being a good and trusted kernel
component is true when you are passing around object pointers…

My 2 cents (and not worth even that!)

Dave Cattley
Consulting Engineer
Systems Software Development

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of David Craig
Sent: Tuesday, October 07, 2008 12:41 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

This is not a ‘false positive’. It is an error to call that function with
an invalid handle. Verifier checks it and will break into the debugger when

that rule is violated. Passing random addresses to kernel functions is a
very bad idea. It can produce side effects that may not be seen for some
time and can occur in places where there would appear to be no relation to
the causing error. This technique of letting the OS catch my errors is a
user mode way of thinking that MUST be avoided in kernel mode. In kernel
mode you are a ‘part’ of the OS with all the powers of the OS and nothing
you do should allow techniques of this sort.

wrote in message news:xxxxx@ntdev…
>I wanted to post a follow-up on my experience running the code I posted
>earlier. It turns out that the behavior of ObReferenceObjectByHandle
>depends on whether the driver verifier is enabled. When the driver verifier

>is not enabled, passing an invalid handle to ObReferenceObjectByHandle
>returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform
>an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH
>(note: in both cases I use UserMode for the access mode parameter). These
>observations match the documentation in the DDK.
>
> However, things are different if the driver verifier is enabled. Passing
> an invalid handle or attempting to perform and invalid typecast will not
> return the expected status code (regardless of the AccessMode). Instead,
> the verifier will trigger a BSOD (code c4, error: c3).
>
> I am not sure why the verifier team chose this behavior. To me this seems
> like a false positive, and will be relevant only if the callers of
> ObReferenceObjectByHandle disregard the return value.
>
> There are several options to deal with this problem:
>
> 1. Disable the verifier:
>
> Not preferable. The verifier has helped me find a lot of problems.
>
> 2. Use MmGetSystemRoutineAddress to obtain the real address of
> ObReferenceObjectByHandle. Note than any regular calls to
> ObReferenceObjectByHandle use the driver verifier’s version of this
> function when the verifier is enabled. I read somewhere that this
> technique may not work on Vista and above. This is not a problem for me,
> since our highest target platform in 2003-x64. I’ve tested on windows
> 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.
>
> --aydan
>


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

Yes, that’s precisely right.

ObReferenceObjectByHandle is the moral equivalent of ProbeForXxx in a try/except. It’s how you validate and capture a user supplied handle and turn it into a validated and guaranteed to remain valid object pointer (iff the user handle meets the stated requirements).

Obviously, as with a probe, you need to “do your homework” and perform the validation *correctly*. However, ObReferenceObjectByHandle is to user handles as a probe is to user memory access; they are unavoidable if you are dealing with these conditions. You simply don’t have a chance *but* to use them if you want to write your program correctly.

  • S

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of David R. Cattley
Sent: Tuesday, October 07, 2008 12:59 PM
To: Windows System Software Devs Interest List
Subject: RE: [ntdev] Problems with ObReferenceObject and ObDerefenceObject

It seems perfectly reasonable to me to pass a ‘value’ into
ObReferenceObjectByHandle() and expect the OS to validate it and only return
an error if the ‘value’ is not a handle, not a handle to the correct type,
or cannot be acquired with the requested access rights. This is exactly
what the function is for. Handles are by their very nature a type of mildly
untrusted identifier with (mostly) safe semantics provided by
ObReferenceObjectByHandle(). This is exactly the case where verifier
throwing a nutty is over the top. The function returns a status. The
contract is not in violation if I pass in 0xDEADBEEF for a handle and it
returns STATUS_INVALID_HANDLE (or STATUS_YOU_SHOULD_EAT_VEGETARIAN, or any
other status).

Now everything you said in reference to being a good and trusted kernel
component is true when you are passing around object pointers…

My 2 cents (and not worth even that!)

Dave Cattley
Consulting Engineer
Systems Software Development

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of David Craig
Sent: Tuesday, October 07, 2008 12:41 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

This is not a ‘false positive’. It is an error to call that function with
an invalid handle. Verifier checks it and will break into the debugger when

that rule is violated. Passing random addresses to kernel functions is a
very bad idea. It can produce side effects that may not be seen for some
time and can occur in places where there would appear to be no relation to
the causing error. This technique of letting the OS catch my errors is a
user mode way of thinking that MUST be avoided in kernel mode. In kernel
mode you are a ‘part’ of the OS with all the powers of the OS and nothing
you do should allow techniques of this sort.

wrote in message news:xxxxx@ntdev…
>I wanted to post a follow-up on my experience running the code I posted
>earlier. It turns out that the behavior of ObReferenceObjectByHandle
>depends on whether the driver verifier is enabled. When the driver verifier

>is not enabled, passing an invalid handle to ObReferenceObjectByHandle
>returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform
>an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH
>(note: in both cases I use UserMode for the access mode parameter). These
>observations match the documentation in the DDK.
>
> However, things are different if the driver verifier is enabled. Passing
> an invalid handle or attempting to perform and invalid typecast will not
> return the expected status code (regardless of the AccessMode). Instead,
> the verifier will trigger a BSOD (code c4, error: c3).
>
> I am not sure why the verifier team chose this behavior. To me this seems
> like a false positive, and will be relevant only if the callers of
> ObReferenceObjectByHandle disregard the return value.
>
> There are several options to deal with this problem:
>
> 1. Disable the verifier:
>
> Not preferable. The verifier has helped me find a lot of problems.
>
> 2. Use MmGetSystemRoutineAddress to obtain the real address of
> ObReferenceObjectByHandle. Note than any regular calls to
> ObReferenceObjectByHandle use the driver verifier’s version of this
> function when the verifier is enabled. I read somewhere that this
> technique may not work on Vista and above. This is not a problem for me,
> since our highest target platform in 2003-x64. I’ve tested on windows
> 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.
>
> --aydan
>


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


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

This behavior is very easy to verify. Simply run both cases with and without a verifier:

  • deref a good handle but use the wrong object type
  • detef a completely bogus handle

Without the verifier, the system handles it just fine (as it should). With the verifier you get BSOD.

I like the driver verifier and run it all the time, but in this case I consider its behavior to be a false positive. This behavior makes sense only if one assumes that callers of ObReferenceObjectByHandle never check the return status. We all have done this at one point or another, but if this was the intention then there should be a flag to enable/disable this check.

–aydan

IIRC, this option was initially added for internal debugging – to ensure that applications are not passing invalid handles and to bugcheck when that occurs (to allow that problem to be fixed). As has already been described, it’s definitely “speculative.”

I assume you’re seeing this on Vista??

I BELIEVE (I can’t guarantee, but I believe) that you can shut this check off by disabling the “Security Checks” option in verifier. This category of checks includes a number of similarly speculative checks.

Remember: Starting AT LEAST on Vista, Driver Verifier has started to include checks that aren’t always guaranteed to be errors. It has started to include checks, like this one, that are speculative. I support this work VERY strongly… but it means that you need to be careful in what you enable and how you interpret the results.

Peter
OSR

Personally, I would have much preferred if verifier just used the raise user exception mechanism to make the exception show up in the app that sent the handle, as opposed to bugchecking the system. (The kernel mode caller must at least be in the same process if you’re referencing a user handle. Suppose there might be some corner cases where you have two threads in a user process, and you pass the handle between the two before referencing it which would cause the user exception to be raised in the wrong thread, but it seems like less of a likely corner case than this, which frequently happens. In either case, it’s a development/debugging aid only…)

In other words, simply use the same framework that appverifier uses for making NtClose on bad handles cause user mode exceptions with driver verifier, instead of bugchecking the entire system in a nonrecoverable way that makes automated fuzz testing a pain.

The problem with bringing the whole system down is that now you can’t do fuzz testing against your drivers with verifier enabled, which is… not a particularly good thing at all, as things like special pool are important to catch overruns and other corruption issues that might result, depending on the nature of a bug.

(I haven’t checked to verify that you can indeed disable this particular check with turning off “security checks”. I hope so! I would also hope that nothing in that same category of checks is something under the “always a bug” class, i.e., that you are only disabling speculative checks. Otherwise, again, that sucks, as that destroys the ability to use some of the additional checks of verifier in conjunction with fuzz / bogus data testing.)

  • S

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Tuesday, October 07, 2008 2:15 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

IIRC, this option was initially added for internal debugging – to ensure that applications are not passing invalid handles and to bugcheck when that occurs (to allow that problem to be fixed). As has already been described, it’s definitely “speculative.”

I assume you’re seeing this on Vista??

I BELIEVE (I can’t guarantee, but I believe) that you can shut this check off by disabling the “Security Checks” option in verifier. This category of checks includes a number of similarly speculative checks.

Remember: Starting AT LEAST on Vista, Driver Verifier has started to include checks that aren’t always guaranteed to be errors. It has started to include checks, like this one, that are speculative. I support this work VERY strongly… but it means that you need to be careful in what you enable and how you interpret the results.

Peter
OSR


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

I asked the DV dev what is going on here, hopefully I can get an answer that I can post back to the group

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@osr.com
Sent: Tuesday, October 07, 2008 11:15 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Problems with ObReferenceObject and ObDerefenceObject

IIRC, this option was initially added for internal debugging – to ensure that applications are not passing invalid handles and to bugcheck when that occurs (to allow that problem to be fixed). As has already been described, it’s definitely “speculative.”

I assume you’re seeing this on Vista??

I BELIEVE (I can’t guarantee, but I believe) that you can shut this check off by disabling the “Security Checks” option in verifier. This category of checks includes a number of similarly speculative checks.

Remember: Starting AT LEAST on Vista, Driver Verifier has started to include checks that aren’t always guaranteed to be errors. It has started to include checks, like this one, that are speculative. I support this work VERY strongly… but it means that you need to be careful in what you enable and how you interpret the results.

Peter
OSR


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

I’ve bumped into this check before so it’s been there for a while and not
new to Vista. I’d guess definitely at least XP, though I’d need to verify.

Passing an invalid handle or attempting to perform and invalid typecast
will not return the expected status code (regardless of the >AccessMode)

Are you sure? A quick uf nt!VerifierReferenceObjectByHandle (thank goodness
the “x” command still takes wildcards) shows that it does indeed make a
decision to bugcheck or not based on this parameter. Checking to see if the
current thread is a system thread is also in there. I should note that I’m
basing this on my XP x64 SP2 workstation, so YMMV (and if the DV dev
responds you’ll get an authoritative answer).

Note than any regular calls to ObReferenceObjectByHandle use the driver
verifier’s version of this function when the verifier is enabled. >I read
somewhere that this technique may not work on Vista and above.

Right, this loophole has been fixed on Vista. MmGetSystemRoutineAddress is
now also thunked and will return the Verifier version of the routine.

-scott


Scott Noone
Software Engineer
OSR Open Systems Resources, Inc.
http://www.osronline.com

wrote in message news:xxxxx@ntdev…
>I wanted to post a follow-up on my experience running the code I posted
>earlier. It turns out that the behavior of ObReferenceObjectByHandle
>depends on whether the driver verifier is enabled. When the driver verifier
>is not enabled, passing an invalid handle to ObReferenceObjectByHandle
>returns with STATUS_INVALID_HANDLE. Similarly, if you try to perform
>an invalid typecast, the function will return STATUS_OBJECT_TYPE_MISMATCH
>(note: in both cases I use UserMode for the access mode parameter). These
>observations match the documentation in the DDK.
>
> However, things are different if the driver verifier is enabled. Passing
> an invalid handle or attempting to perform and invalid typecast will not
> return the expected status code (regardless of the AccessMode). Instead,
> the verifier will trigger a BSOD (code c4, error: c3).
>
> I am not sure why the verifier team chose this behavior. To me this seems
> like a false positive, and will be relevant only if the callers of
> ObReferenceObjectByHandle disregard the return value.
>
> There are several options to deal with this problem:
>
> 1. Disable the verifier:
>
> Not preferable. The verifier has helped me find a lot of problems.
>
> 2. Use MmGetSystemRoutineAddress to obtain the real address of
> ObReferenceObjectByHandle. Note than any regular calls to
> ObReferenceObjectByHandle use the driver verifier’s version of this
> function when the verifier is enabled. I read somewhere that this
> technique may not work on Vista and above. This is not a problem for me,
> since our highest target platform in 2003-x64. I’ve tested on windows
> 2000, XP, 2003 (x86 and x64) and it seems to work on these platforms.
>
> --aydan
>

Hi everyone,

In the current implementation, VerifierObfDereferenceObject breaks for STATUS_INVALID_HANDLE and STATUS_OBJECT_TYPE_MISMATCH situations in one of these two cases:

  1. AccessMode == KernelMode.
  2. PsIsSystemThread (PsGetCurrentThread ()) == TRUE.

From the description above, I understand that Aydan’s driver falls under case #2.

As others have said in this thread, the OS supports validation of handle values coming from applications - i.e. handle referenced with AccessMode != UserMode and in the context of an app (i.e. PsIsSystemThread (PsGetCurrentThread ()) != TRUE). Verifier allows those too.

However, the OS doesn’t support referencing someone else’s handles, behind the back of the handle owner. We have heard over the last few weeks that a few drivers still want to perform such references. I recommend following up with MS developer support, explaining the business need, and asking for OS support for performing such handle references in a safe manner. Until such support will get added, I am not aware of a safe/supported way to achieve what Aydan was trying to achieve.

We are considering changing Driver Verifier in future OS versions to allow users to disable this VerifierObfDereferenceObject break. In the current implementation, the user would have to disable the entire Verifier to ignore these breaks. We are looking into tying this break to the Verifier Miscellaneous Checks, allowing these users to still take advantage of the rest of Verifier, and disable “just” the Miscellaneous Checks.

Please feel free to follow-up on my microsoft.com address, or on verifier @ microsoft.com about this issue or about anything else related to Verifier.

Thanks,
Dan

Correction:

As others have said in this thread, the OS supports validation of handle values
coming from applications - i.e. handle referenced with AccessMode != KernelMode
and in the context of an app (i.e. PsIsSystemThread (PsGetCurrentThread ()) !=
TRUE). Verifier allows those too.