- Fix definitions of OB_QUERYNAME_METHOD and OB_OKAYTOCLOSE_METHOD. Add fixme for OB_SECURITY_METHOD, will change it later.

- Simplified NtDuplicateObject to a simple stub around ObDuplicateObject, there is no need to have lengthy hacks for NtCurrentProcess/NtCurrentThread().
- Cleanup ObDuplicateObject to use normal Ob routines instead of grovelling through handle table structures and manually touching the handle count: It now calls ObpIncrementHandleCount and all the underlying operations are done on the duplicated handle. Also access state creation is done if the duplicated handle has different desired access.

svn path=/trunk/; revision=22303
This commit is contained in:
Alex Ionescu 2006-06-11 06:52:56 +00:00
parent d6a84751c7
commit 259db9be47
8 changed files with 252 additions and 279 deletions

View file

@ -215,7 +215,7 @@ typedef NTSTATUS
(NTAPI *OB_SECURITY_METHOD)( (NTAPI *OB_SECURITY_METHOD)(
IN PVOID Object, IN PVOID Object,
IN SECURITY_OPERATION_CODE OperationType, IN SECURITY_OPERATION_CODE OperationType,
IN SECURITY_INFORMATION SecurityInformation, IN SECURITY_INFORMATION SecurityInformation, // FIXME: <= should be a pointer
IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN OUT PULONG CapturedLength, IN OUT PULONG CapturedLength,
IN OUT PSECURITY_DESCRIPTOR *ObjectSecurityDescriptor, IN OUT PSECURITY_DESCRIPTOR *ObjectSecurityDescriptor,
@ -229,14 +229,16 @@ typedef NTSTATUS
IN BOOLEAN HasObjectName, IN BOOLEAN HasObjectName,
OUT POBJECT_NAME_INFORMATION ObjectNameInfo, OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
IN ULONG Length, IN ULONG Length,
OUT PULONG ReturnLength OUT PULONG ReturnLength,
IN KPROCESSOR_MODE AccessMode
); );
typedef NTSTATUS typedef NTSTATUS
(NTAPI *OB_OKAYTOCLOSE_METHOD)( (NTAPI *OB_OKAYTOCLOSE_METHOD)(
IN PEPROCESS Process OPTIONAL, IN PEPROCESS Process OPTIONAL,
IN PVOID Object, IN PVOID Object,
IN HANDLE Handle IN HANDLE Handle,
IN KPROCESSOR_MODE AccessMode
); );
#else #else

View file

@ -467,7 +467,8 @@ CmiObjectQueryName (PVOID ObjectBody,
IN BOOLEAN HasObjectName, IN BOOLEAN HasObjectName,
POBJECT_NAME_INFORMATION ObjectNameInfo, POBJECT_NAME_INFORMATION ObjectNameInfo,
ULONG Length, ULONG Length,
PULONG ReturnLength); PULONG ReturnLength,
IN KPROCESSOR_MODE PreviousMode);
NTSTATUS NTSTATUS
CmiImportHiveBins(PREGISTRY_HIVE Hive, CmiImportHiveBins(PREGISTRY_HIVE Hive,

View file

@ -691,7 +691,8 @@ CmiObjectQueryName (PVOID ObjectBody,
IN BOOLEAN HasName, IN BOOLEAN HasName,
POBJECT_NAME_INFORMATION ObjectNameInfo, POBJECT_NAME_INFORMATION ObjectNameInfo,
ULONG Length, ULONG Length,
PULONG ReturnLength) PULONG ReturnLength,
IN KPROCESSOR_MODE PreviousMode)
{ {
PKEY_OBJECT KeyObject; PKEY_OBJECT KeyObject;
NTSTATUS Status; NTSTATUS Status;

View file

@ -518,7 +518,8 @@ IopQueryNameFile(
IN BOOLEAN HasName, IN BOOLEAN HasName,
POBJECT_NAME_INFORMATION ObjectNameInfo, POBJECT_NAME_INFORMATION ObjectNameInfo,
ULONG Length, ULONG Length,
PULONG ReturnLength PULONG ReturnLength,
IN KPROCESSOR_MODE PreviousMode
); );
VOID VOID

View file

@ -121,18 +121,6 @@ ObpDeleteNameCheck(
IN PVOID Object IN PVOID Object
); );
NTSTATUS
NTAPI
ObDuplicateObject(
PEPROCESS SourceProcess,
PEPROCESS TargetProcess,
HANDLE SourceHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG HandleAttributes,
ULONG Options
);
VOID VOID
NTAPI NTAPI
ObQueryDeviceMapInformation( ObQueryDeviceMapInformation(

View file

@ -411,7 +411,8 @@ IopQueryNameFile(PVOID ObjectBody,
IN BOOLEAN HasName, IN BOOLEAN HasName,
POBJECT_NAME_INFORMATION ObjectNameInfo, POBJECT_NAME_INFORMATION ObjectNameInfo,
ULONG Length, ULONG Length,
PULONG ReturnLength) PULONG ReturnLength,
IN KPROCESSOR_MODE PreviousMode)
{ {
POBJECT_NAME_INFORMATION LocalInfo; POBJECT_NAME_INFORMATION LocalInfo;
PFILE_OBJECT FileObject; PFILE_OBJECT FileObject;

View file

@ -196,7 +196,8 @@ ObpCloseHandleTableEntry(IN PHANDLE_TABLE HandleTable,
/* Call it and check if it's not letting us close it */ /* Call it and check if it's not letting us close it */
if (!ObjectType->TypeInfo.OkayToCloseProcedure(PsGetCurrentProcess(), if (!ObjectType->TypeInfo.OkayToCloseProcedure(PsGetCurrentProcess(),
Body, Body,
Handle)) Handle,
AccessMode))
{ {
/* Fail */ /* Fail */
ExUnlockHandleTableEntry(HandleTable, HandleEntry); ExUnlockHandleTableEntry(HandleTable, HandleEntry);
@ -1188,158 +1189,201 @@ ObKillProcess(IN PEPROCESS Process)
NTSTATUS NTSTATUS
NTAPI NTAPI
ObDuplicateObject(PEPROCESS SourceProcess, ObDuplicateObject(IN PEPROCESS SourceProcess,
PEPROCESS TargetProcess, IN HANDLE SourceHandle,
HANDLE SourceHandle, IN PEPROCESS TargetProcess OPTIONAL,
PHANDLE TargetHandle, IN PHANDLE TargetHandle OPTIONAL,
ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
ULONG HandleAttributes, IN ULONG HandleAttributes,
ULONG Options) IN ULONG Options,
IN KPROCESSOR_MODE PreviousMode)
{ {
PHANDLE_TABLE_ENTRY SourceHandleEntry;
HANDLE_TABLE_ENTRY NewHandleEntry; HANDLE_TABLE_ENTRY NewHandleEntry;
BOOLEAN AttachedToProcess = FALSE; BOOLEAN AttachedToProcess = FALSE;
PVOID ObjectBody; PVOID SourceObject;
POBJECT_HEADER ObjectHeader; POBJECT_HEADER ObjectHeader;
ULONG NewHandleCount; POBJECT_TYPE ObjectType;
HANDLE NewTargetHandle; HANDLE NewHandle;
PEPROCESS CurrentProcess;
KAPC_STATE ApcState; KAPC_STATE ApcState;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
ACCESS_MASK TargetAccess, SourceAccess;
ACCESS_STATE AccessState;
PACCESS_STATE PassedAccessState = NULL;
AUX_DATA AuxData;
PHANDLE_TABLE HandleTable = NULL;
OBJECT_HANDLE_INFORMATION HandleInformation;
PAGED_CODE(); PAGED_CODE();
OBTRACE("OBTRACE - %s - Duplicating handle: %lx for %p into %p\n",
__FUNCTION__,
SourceHandle,
SourceProcess,
TargetProcess);
if(SourceProcess == NULL || /* Check if we're not in the source process */
ObIsKernelHandle(SourceHandle, ExGetPreviousMode())) if (SourceProcess != PsGetCurrentProcess())
{ {
SourceProcess = PsInitialSystemProcess; /* Attach to it */
SourceHandle = ObKernelHandleToHandle(SourceHandle); KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
}
CurrentProcess = PsGetCurrentProcess();
KeEnterCriticalRegion();
if (SourceProcess != CurrentProcess)
{
KeStackAttachProcess(&SourceProcess->Pcb,
&ApcState);
AttachedToProcess = TRUE; AttachedToProcess = TRUE;
} }
SourceHandleEntry = ExMapHandleToPointer(SourceProcess->ObjectTable,
SourceHandle);
if (SourceHandleEntry == NULL)
{
if (AttachedToProcess)
{
KeUnstackDetachProcess(&ApcState);
}
KeLeaveCriticalRegion(); /* Now reference the source handle */
return STATUS_INVALID_HANDLE; Status = ObReferenceObjectByHandle(SourceHandle,
} 0,
NULL,
ObjectHeader = EX_HTE_TO_HDR(SourceHandleEntry); PreviousMode,
ObjectBody = &ObjectHeader->Body; (PVOID*)&SourceObject,
&HandleInformation);
NewHandleEntry.Object = SourceHandleEntry->Object;
if(HandleAttributes & OBJ_INHERIT)
NewHandleEntry.ObAttributes |= EX_HANDLE_ENTRY_INHERITABLE;
else
NewHandleEntry.ObAttributes &= ~EX_HANDLE_ENTRY_INHERITABLE;
NewHandleEntry.GrantedAccess = ((Options & DUPLICATE_SAME_ACCESS) ?
SourceHandleEntry->GrantedAccess :
DesiredAccess);
if (Options & DUPLICATE_SAME_ACCESS)
{
NewHandleEntry.GrantedAccess = SourceHandleEntry->GrantedAccess;
}
else
{
if (DesiredAccess & GENERIC_ACCESS)
{
RtlMapGenericMask(&DesiredAccess,
&ObjectHeader->Type->TypeInfo.GenericMapping);
}
NewHandleEntry.GrantedAccess = DesiredAccess;
}
/* reference the object so it doesn't get deleted after releasing the lock
and before creating a new handle for it */
ObReferenceObject(ObjectBody);
/* increment the handle count of the object, it should always be >= 2 because
we're holding a handle lock to this object! if the new handle count was
1 here, we're in big trouble... it would've been safe to increment and
check the handle count without using interlocked functions because the
entry is locked, which means the handle count can't change. */
NewHandleCount = InterlockedIncrement(&ObjectHeader->HandleCount);
ASSERT(NewHandleCount >= 2);
ExUnlockHandleTableEntry(SourceProcess->ObjectTable,
SourceHandleEntry);
/* Check if we were attached */
if (AttachedToProcess) if (AttachedToProcess)
{ {
/* We can safely detach now */
KeUnstackDetachProcess(&ApcState); KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE; AttachedToProcess = FALSE;
} }
if (TargetProcess != CurrentProcess) /* Fail if we couldn't reference it */
if (!NT_SUCCESS(Status)) return Status;
/* Get the source access */
SourceAccess = HandleInformation.GrantedAccess;
/* Check if we're not in the target process */
if (TargetProcess != PsGetCurrentProcess())
{ {
KeStackAttachProcess(&TargetProcess->Pcb, /* Attach to it */
&ApcState); KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
AttachedToProcess = TRUE; AttachedToProcess = TRUE;
} }
/* attempt to create the new handle */ /* Check if we're duplicating the attributes */
NewTargetHandle = ExCreateHandle(TargetProcess->ObjectTable, if (Options & DUPLICATE_SAME_ATTRIBUTES)
&NewHandleEntry); {
/* Duplicate them */
HandleAttributes = HandleInformation.HandleAttributes;
}
/* Check if we're duplicating the access */
if (Options & DUPLICATE_SAME_ACCESS) DesiredAccess = SourceAccess;
/* Get object data */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(SourceObject);
ObjectType = ObjectHeader->Type;
/* Fill out the entry */
NewHandleEntry.Object = ObjectHeader;
NewHandleEntry.ObAttributes |= HandleAttributes &
(EX_HANDLE_ENTRY_PROTECTFROMCLOSE |
EX_HANDLE_ENTRY_INHERITABLE |
EX_HANDLE_ENTRY_AUDITONCLOSE);
/* Check if we're using a generic mask */
if (DesiredAccess & GENERIC_ACCESS)
{
/* Map it */
RtlMapGenericMask(&DesiredAccess, &ObjectType->TypeInfo.GenericMapping);
}
/* Set the target access */
TargetAccess = DesiredAccess;
NewHandleEntry.GrantedAccess = TargetAccess;
/* Check if we're asking for new access */
if (TargetAccess & ~SourceAccess)
{
/* We are. We need the security procedure to validate this */
if (ObjectType->TypeInfo.SecurityProcedure == SeDefaultObjectMethod)
{
/* Use our built-in access state */
PassedAccessState = &AccessState;
Status = SeCreateAccessState(&AccessState,
&AuxData,
TargetAccess,
&ObjectType->TypeInfo.GenericMapping);
}
else
{
/* Otherwise we can't allow this privilege elevation */
Status = STATUS_ACCESS_DENIED;
}
}
else
{
/* We don't need an access state */
Status = STATUS_SUCCESS;
}
/* Make sure the access state was created OK */
if (NT_SUCCESS(Status))
{
/* Add a new handle */
Status = ObpIncrementHandleCount(SourceObject,
PassedAccessState,
PreviousMode,
HandleAttributes,
PsGetCurrentProcess(),
ObDuplicateHandle);
/* Set the handle table, now that we know this handle was added */
HandleTable = PsGetCurrentProcess()->ObjectTable;
}
/* Check if we were attached */
if (AttachedToProcess) if (AttachedToProcess)
{ {
/* We can safely detach now */
KeUnstackDetachProcess(&ApcState); KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE; AttachedToProcess = FALSE;
} }
if (NewTargetHandle != NULL) /* Check if we have to close the source handle */
if (Options & DUPLICATE_CLOSE_SOURCE)
{ {
if (Options & DUPLICATE_CLOSE_SOURCE) /* Attach and close */
{ KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
if (SourceProcess != CurrentProcess) NtClose(SourceHandle);
{ KeUnstackDetachProcess(&ApcState);
KeStackAttachProcess(&SourceProcess->Pcb,
&ApcState);
AttachedToProcess = TRUE;
}
/* delete the source handle */
NtClose(SourceHandle);
if (AttachedToProcess)
{
KeUnstackDetachProcess(&ApcState);
}
}
ObDereferenceObject(ObjectBody);
*TargetHandle = NewTargetHandle;
}
else
{
/* decrement the handle count we previously incremented, but don't call the
closing procedure because we're not closing a handle! */
if(InterlockedDecrement(&ObjectHeader->HandleCount) == 0)
{
ObDereferenceObject(ObjectBody);
}
ObDereferenceObject(ObjectBody);
Status = STATUS_UNSUCCESSFUL;
} }
KeLeaveCriticalRegion(); /* Check if we had an access state */
if (PassedAccessState) SeDeleteAccessState(PassedAccessState);
/* Now check if incrementing actually failed */
if (!NT_SUCCESS(Status))
{
/* Dereference the source object */
ObDereferenceObject(SourceObject);
return Status;
}
/* Now create the handle */
ObDereferenceObject(SourceObject);
NewHandle = ExCreateHandle(HandleTable, &NewHandleEntry);
if (!NewHandle)
{
/* Undo the increment */
ObpDecrementHandleCount(SourceObject,
TargetProcess,
TargetAccess);
/* Deference the object and set failure status */
ObDereferenceObject(SourceObject);
Status = STATUS_INSUFFICIENT_RESOURCES;
}
/* Return the handle */
if (TargetHandle) *TargetHandle = NewHandle;
/* Return status */
OBTRACE("OBTRACE - %s - Duplicated handle: %lx for %p into %p. Source: %p HC PC %lx %lx\n",
__FUNCTION__,
NewHandle,
SourceProcess,
TargetProcess,
SourceObject,
ObjectHeader->PointerCount,
ObjectHeader->HandleCount);
return Status; return Status;
} }
@ -1934,184 +1978,118 @@ NtClose(IN HANDLE Handle)
return ObpCloseHandle(Handle, ExGetPreviousMode()); return ObpCloseHandle(Handle, ExGetPreviousMode());
} }
/* NTSTATUS
* @implemented NTAPI
*/ NtDuplicateObject(IN HANDLE SourceProcessHandle,
NTSTATUS STDCALL IN HANDLE SourceHandle,
NtDuplicateObject (IN HANDLE SourceProcessHandle, IN HANDLE TargetProcessHandle OPTIONAL,
IN HANDLE SourceHandle, OUT PHANDLE TargetHandle OPTIONAL,
IN HANDLE TargetProcessHandle, IN ACCESS_MASK DesiredAccess,
OUT PHANDLE TargetHandle OPTIONAL, IN ULONG HandleAttributes,
IN ACCESS_MASK DesiredAccess, IN ULONG Options)
IN ULONG HandleAttributes,
IN ULONG Options)
{ {
PEPROCESS SourceProcess; PEPROCESS SourceProcess, TargetProcess, Target;
PEPROCESS TargetProcess;
PEPROCESS CurrentProcess;
HANDLE hTarget; HANDLE hTarget;
BOOLEAN AttachedToProcess = FALSE; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
KPROCESSOR_MODE PreviousMode;
KAPC_STATE ApcState;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
ACCESS_STATE AccessState;
AUX_DATA AuxData;
PACCESS_STATE PassedAccessState = NULL;
PAGED_CODE(); PAGED_CODE();
OBTRACE("OBTRACE - %s - Duplicating handle: %lx for %lx into %lx.\n",
__FUNCTION__,
SourceHandle,
SourceProcessHandle,
TargetProcessHandle);
PreviousMode = ExGetPreviousMode(); if((TargetHandle) && (PreviousMode != KernelMode))
if(TargetHandle != NULL && PreviousMode != KernelMode)
{ {
/* Enter SEH */
_SEH_TRY _SEH_TRY
{ {
/* Probe the handle */
ProbeForWriteHandle(TargetHandle); ProbeForWriteHandle(TargetHandle);
} }
_SEH_HANDLE _SEH_HANDLE
{ {
/* Get the exception status */
Status = _SEH_GetExceptionCode(); Status = _SEH_GetExceptionCode();
} }
_SEH_END; _SEH_END;
if(!NT_SUCCESS(Status)) /* Fail if the pointer was invalid */
{ if (!NT_SUCCESS(Status)) return Status;
return Status;
}
} }
/* Now reference the input handle */
Status = ObReferenceObjectByHandle(SourceProcessHandle, Status = ObReferenceObjectByHandle(SourceProcessHandle,
PROCESS_DUP_HANDLE, PROCESS_DUP_HANDLE,
NULL, PsProcessType,
PreviousMode, PreviousMode,
(PVOID*)&SourceProcess, (PVOID*)&SourceProcess,
NULL); NULL);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status)) return(Status);
/* Check if got a target handle */
if (TargetProcessHandle)
{ {
return(Status); /* Now reference the output handle */
} Status = ObReferenceObjectByHandle(TargetProcessHandle,
PROCESS_DUP_HANDLE,
Status = ObReferenceObjectByHandle(TargetProcessHandle, PsProcessType,
PROCESS_DUP_HANDLE, PreviousMode,
NULL, (PVOID*)&TargetProcess,
PreviousMode, NULL);
(PVOID*)&TargetProcess, if (NT_SUCCESS(Status))
NULL);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(SourceProcess);
return(Status);
}
CurrentProcess = PsGetCurrentProcess();
/* Check for magic handle first */
if (SourceHandle == NtCurrentThread() ||
SourceHandle == NtCurrentProcess())
{
PVOID ObjectBody;
POBJECT_TYPE ObjectType;
ObjectType = (SourceHandle == NtCurrentThread()) ? PsThreadType : PsProcessType;
Status = ObReferenceObjectByHandle(SourceHandle,
0,
ObjectType,
PreviousMode,
&ObjectBody,
NULL);
if(NT_SUCCESS(Status))
{ {
if (Options & DUPLICATE_SAME_ACCESS) /* Use this target process */
{ Target = TargetProcess;
/* grant all access rights */ }
DesiredAccess = ((ObjectType == PsThreadType) ? THREAD_ALL_ACCESS : PROCESS_ALL_ACCESS); else
} {
else /* No target process */
{ Target = NULL;
if (DesiredAccess & GENERIC_ACCESS)
{
RtlMapGenericMask(&DesiredAccess,
&ObjectType->TypeInfo.GenericMapping);
}
}
if (TargetProcess != CurrentProcess)
{
KeStackAttachProcess(&TargetProcess->Pcb,
&ApcState);
AttachedToProcess = TRUE;
}
/* Use our built-in access state */
PassedAccessState = &AccessState;
Status = SeCreateAccessState(&AccessState,
&AuxData,
DesiredAccess,
&ObjectType->TypeInfo.GenericMapping);
/* Add a new handle */
Status = ObpIncrementHandleCount(ObjectBody,
PassedAccessState,
PreviousMode,
HandleAttributes,
PsGetCurrentProcess(),
ObDuplicateHandle);
if (AttachedToProcess)
{
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
ObDereferenceObject(ObjectBody);
if (Options & DUPLICATE_CLOSE_SOURCE)
{
if (SourceProcess != CurrentProcess)
{
KeStackAttachProcess(&SourceProcess->Pcb,
&ApcState);
AttachedToProcess = TRUE;
}
NtClose(SourceHandle);
if (AttachedToProcess)
{
KeUnstackDetachProcess(&ApcState);
}
}
} }
} }
else else
{ {
Status = ObDuplicateObject(SourceProcess, /* No target process */
TargetProcess, Status = STATUS_SUCCESS;
SourceHandle, Target = NULL;
&hTarget,
DesiredAccess,
HandleAttributes,
Options);
} }
ObDereferenceObject(TargetProcess); /* Call the internal routine */
ObDereferenceObject(SourceProcess); Status = ObDuplicateObject(SourceProcess,
SourceHandle,
Target,
&hTarget,
DesiredAccess,
HandleAttributes,
Options,
PreviousMode);
if(NT_SUCCESS(Status) && TargetHandle != NULL) /* Check if the caller wanted the return handle */
if (TargetHandle)
{ {
/* Protect the write to user mode */
_SEH_TRY _SEH_TRY
{ {
/* Write the new handle */
*TargetHandle = hTarget; *TargetHandle = hTarget;
} }
_SEH_HANDLE _SEH_HANDLE
{ {
/* Otherwise, get the exception code */
Status = _SEH_GetExceptionCode(); Status = _SEH_GetExceptionCode();
} }
_SEH_END; _SEH_END;
} }
/* Dereference the processes */
OBTRACE("OBTRACE - %s - Duplicated handle: %lx into %lx S %lx\n",
__FUNCTION__,
hTarget,
TargetProcessHandle,
Status);
ObDereferenceObject(TargetProcess);
ObDereferenceObject(SourceProcess);
return Status; return Status;
} }
/* EOF */ /* EOF */

View file

@ -394,7 +394,7 @@ Next:
/* PUBLIC FUNCTIONS *********************************************************/ /* PUBLIC FUNCTIONS *********************************************************/
NTSTATUS NTSTATUS
STDCALL NTAPI
ObQueryNameString(IN PVOID Object, ObQueryNameString(IN PVOID Object,
OUT POBJECT_NAME_INFORMATION ObjectNameInfo, OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
IN ULONG Length, IN ULONG Length,
@ -418,7 +418,8 @@ ObQueryNameString(IN PVOID Object,
TRUE, //fixme TRUE, //fixme
ObjectNameInfo, ObjectNameInfo,
Length, Length,
ReturnLength); ReturnLength,
KernelMode);
} }
/* Check if the object doesn't even have a name */ /* Check if the object doesn't even have a name */