BOOLEAN obtest( void ) { ULONG i; HANDLE Handles[ 2 ]; NTSTATUS Status; OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; ObpDumpObjectTable( ObpGetObjectTable(), NULL ); RtlInitString( &ObjectTypeAName, "ObjectTypeA" ); RtlInitString( &ObjectTypeBName, "ObjectTypeB" ); RtlZeroMemory( &ObjectTypeInitializer, sizeof( ObjectTypeInitializer ) ); ObjectTypeInitializer.Length = sizeof( ObjectTypeInitializer ); ObjectTypeInitializer.ValidAccessMask = -1; ObjectTypeInitializer.PoolType = NonPagedPool; ObjectTypeInitializer.MaintainHandleCount = TRUE; ObjectTypeInitializer.DumpProcedure = DumpAProc; ObjectTypeInitializer.OpenProcedure = OpenAProc; ObjectTypeInitializer.CloseProcedure = CloseAProc; ObjectTypeInitializer.DeleteProcedure = DeleteAProc; ObjectTypeInitializer.ParseProcedure = ParseAProc; ObCreateObjectType( &ObjectTypeAName, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &ObjectTypeA ); ObjectTypeInitializer.PoolType = NonPagedPool; ObjectTypeInitializer.MaintainHandleCount = FALSE; ObjectTypeInitializer.GenericMapping = MyGenericMapping; ObjectTypeInitializer.DumpProcedure = DumpBProc; ObjectTypeInitializer.OpenProcedure = NULL; ObjectTypeInitializer.CloseProcedure = NULL; ObjectTypeInitializer.DeleteProcedure = DeleteBProc; ObjectTypeInitializer.ParseProcedure = NULL; ObCreateObjectType( &ObjectTypeBName, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &ObjectTypeB ); ObpDumpTypes( NULL ); RtlInitString( &DirectoryName, "\\MyObjects" ); InitializeObjectAttributes( &DirectoryObjA, &DirectoryName, OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL ); NtCreateDirectoryObject( &DirectoryHandle, 0, &DirectoryObjA ); NtClose( DirectoryHandle ); RtlInitString( &ObjectAName, "\\myobjects\\ObjectA" ); InitializeObjectAttributes( &ObjectAObjA, &ObjectAName, OBJ_CASE_INSENSITIVE, NULL, NULL ); RtlInitString( &ObjectBName, "\\myobjects\\ObjectB" ); InitializeObjectAttributes( &ObjectBObjA, &ObjectBName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = ObCreateObject( KernelMode, ObjectTypeA, &ObjectAObjA, KernelMode, NULL, (ULONG)sizeof( OBJECTTYPEA ), 0L, 0L, (PVOID *)&ObjectBodyA ); ObjectA = (POBJECTTYPEA)ObjectBodyA; ObjectA->TypeALength = sizeof( *ObjectA ); for (i=0; i<4; i++) { ObjectA->Stuff[i] = i+1; } KeInitializeEvent( &ObjectA->Event, NotificationEvent, TRUE ); Status = ObCreateObject( KernelMode, ObjectTypeB, &ObjectBObjA, KernelMode, NULL, (ULONG)sizeof( OBJECTTYPEB ), 0L, 0L, (PVOID *)&ObjectBodyB ); ObjectB = (POBJECTTYPEB)ObjectBodyB; ObjectB->TypeBLength = sizeof( *ObjectB ); for (i=0; i<16; i++) { ObjectB->Stuff[i] = i+1; } KeInitializeSemaphore ( &ObjectB->Semaphore, 2L, 2L ); Status = ObInsertObject( ObjectBodyA, SYNCHRONIZE | 0x3, NULL, 1, &ObjectBodyA, &ObjectHandleA1 ); DbgPrint( "Status: %lx ObjectBodyA: %lx ObjectHandleA1: %lx\n", Status, ObjectBodyA, ObjectHandleA1 ); Status = ObInsertObject( ObjectBodyB, SYNCHRONIZE | 0x1, NULL, 1, &ObjectBodyB, &ObjectHandleB1 ); DbgPrint( "Status: %lx ObjectBodyB: %lx ObjectHandleB1: %lx\n", Status, ObjectBodyB, ObjectHandleB1 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); RtlInitString( &ObjectAName, "\\MyObjects\\ObjectA" ); InitializeObjectAttributes( &ObjectAObjA, &ObjectAName, OBJ_OPENIF, NULL, NULL ); Status = ObCreateObject( KernelMode, ObjectTypeA, &ObjectAObjA, KernelMode, NULL, (ULONG)sizeof( OBJECTTYPEA ), 0L, 0L, (PVOID *)&ObjectBodyA1 ); Status = ObInsertObject( ObjectBodyA1, SYNCHRONIZE | 0x3, NULL, 1, &ObjectBodyA2, &ObjectHandleA2 ); DbgPrint( "Status: %lx ObjectBodyA1: %lx ObjectBodyA2: %lx ObjectHandleA2: %lx\n", Status, ObjectBodyA1, ObjectBodyA2, ObjectHandleA2 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); NtClose( ObjectHandleA2 ); ObDereferenceObject( ObjectBodyA2 ); // ObInsertObject,ObjectPointerBias NtWaitForSingleObject( ObjectHandleB1, TRUE, NULL ); Handles[ 0 ] = ObjectHandleA1; Handles[ 1 ] = ObjectHandleB1; NtWaitForMultipleObjects( 2, Handles, WaitAny, TRUE, NULL ); ObReferenceObjectByHandle( ObjectHandleA1, 0L, ObjectTypeA, KernelMode, &ObjectBodyA, NULL ); ObReferenceObjectByHandle( ObjectHandleB1, 0L, ObjectTypeB, KernelMode, &ObjectBodyB, NULL ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleA1, ObjectBodyA ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleB1, ObjectBodyB ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObReferenceObjectByPointer( ObjectBodyA, 0L, ObjectTypeA, KernelMode ); ObReferenceObjectByPointer( ObjectBodyB, 0L, ObjectTypeB, KernelMode ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); RtlInitString( &ObjectAPathName, "\\MyObjects\\ObjectA" ); RtlInitString( &ObjectBPathName, "\\MyObjects\\ObjectB" ); ObReferenceObjectByName( &ObjectAPathName, OBJ_CASE_INSENSITIVE, 0L, ObjectTypeA, KernelMode, NULL, &ObjectBodyA ); ObReferenceObjectByName( &ObjectBPathName, OBJ_CASE_INSENSITIVE, 0L, ObjectTypeB, KernelMode, NULL, &ObjectBodyB ); DbgPrint( "Reference Name %s = %lx\n", ObjectAPathName.Buffer, ObjectBodyA ); DbgPrint( "Reference Name %s = %lx\n", ObjectBPathName.Buffer, ObjectBodyB ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObDereferenceObject( ObjectBodyA ); // ObInsertObject,ObjectPointerBias ObDereferenceObject( ObjectBodyB ); ObDereferenceObject( ObjectBodyA ); // ObReferenceObjectByHandle ObDereferenceObject( ObjectBodyB ); ObDereferenceObject( ObjectBodyA ); // ObReferenceObjectByPointer ObDereferenceObject( ObjectBodyB ); ObDereferenceObject( ObjectBodyA ); // ObReferenceObjectByName ObDereferenceObject( ObjectBodyB ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); InitializeObjectAttributes( &ObjectAObjA, &ObjectAPathName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ObOpenObjectByName( &ObjectAObjA, 0L, NULL, ObjectTypeA, KernelMode, NULL, &ObjectHandleA2 ); InitializeObjectAttributes( &ObjectBObjA, &ObjectBPathName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ObOpenObjectByName( &ObjectBObjA, 0L, NULL, ObjectTypeB, KernelMode, NULL, &ObjectHandleB2 ); DbgPrint( "Open Object Name %s = %lx\n", ObjectAPathName.Buffer, ObjectHandleA2 ); DbgPrint( "Open Object Name %s = %lx\n", ObjectBPathName.Buffer, ObjectHandleB2 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); NtClose( ObjectHandleA1 ); NtClose( ObjectHandleB1 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObReferenceObjectByHandle( ObjectHandleA2, 0L, ObjectTypeA, KernelMode, &ObjectBodyA, NULL ); ObReferenceObjectByHandle( ObjectHandleB2, 0L, ObjectTypeB, KernelMode, &ObjectBodyB, NULL ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleA2, ObjectBodyA ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleB2, ObjectBodyB ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObOpenObjectByPointer( ObjectBodyA, OBJ_CASE_INSENSITIVE, 0L, NULL, ObjectTypeA, KernelMode, &ObjectHandleA1 ); ObOpenObjectByPointer( ObjectBodyB, OBJ_CASE_INSENSITIVE, 0L, NULL, ObjectTypeB, KernelMode, &ObjectHandleB1 ); DbgPrint( "Open Object Pointer %lx = %lx\n", ObjectBodyA, ObjectHandleA1 ); DbgPrint( "Open Object Pointer %lx = %lx\n", ObjectBodyB, ObjectHandleB1 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObReferenceObjectByHandle( ObjectHandleA1, 0L, ObjectTypeA, KernelMode, &ObjectBodyA, NULL ); ObReferenceObjectByHandle( ObjectHandleB1, 0L, ObjectTypeB, KernelMode, &ObjectBodyB, NULL ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleA1, ObjectBodyA ); DbgPrint( "Reference Handle %lx = %lx\n", ObjectHandleB1, ObjectBodyB ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); ObDereferenceObject( ObjectBodyA ); // ObReferenceObjectByHandle ObDereferenceObject( ObjectBodyB ); ObDereferenceObject( ObjectBodyA ); // ObReferenceObjectByHandle ObDereferenceObject( ObjectBodyB ); NtClose( ObjectHandleA1 ); NtClose( ObjectHandleB1 ); NtClose( ObjectHandleA2 ); NtClose( ObjectHandleB2 ); ObpDumpObjectTable( ObpGetObjectTable(), NULL ); TestFunction = NULL; return( TRUE ); }
NTSTATUS NtClose ( IN HANDLE Handle ) /*++ Routine Description: This function is used to close access to the specified handle Arguments: Handle - Supplies the handle being closed Return Value: An appropriate status value --*/ { PHANDLE_TABLE ObjectTable; PHANDLE_TABLE_ENTRY ObjectTableEntry; PVOID Object; ULONG CapturedGrantedAccess; ULONG CapturedAttributes; POBJECT_HEADER ObjectHeader; POBJECT_TYPE ObjectType; NTSTATUS Status; BOOLEAN AttachedToProcess = FALSE; KAPC_STATE ApcState; // // Protect ourselves from being interrupted while we hold a handle table // entry lock // KeEnterCriticalRegion(); try { #if DBG KIRQL SaveIrql; #endif // DBG ObpValidateIrql( "NtClose" ); ObpBeginTypeSpecificCallOut( SaveIrql ); #if DBG // // On checked builds, check that if the Kernel handle bit is set, then // we're coming from Kernel mode. We should probably fail the call if // bit set && !Kmode // if ((Handle != NtCurrentThread()) && (Handle != NtCurrentProcess())) { ASSERT((Handle < 0 ) ? (KeGetPreviousMode() == KernelMode) : TRUE); } #endif // // For the current process we will grab its handle/object table and // translate the handle to its corresponding table entry. If the // call is successful it also lock down the handle table. But first // check for a kernel handle and attach and use that table if so. // if (IsKernelHandle( Handle, KeGetPreviousMode() )) { Handle = DecodeKernelHandle( Handle ); ObjectTable = ObpKernelHandleTable; // // Go to the system process if we have to // if (PsGetCurrentProcess() != PsInitialSystemProcess) { KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState); AttachedToProcess = TRUE; } } else { ObjectTable = ObpGetObjectTable(); } ObjectTableEntry = ExMapHandleToPointer( ObjectTable, Handle ); // // Check that the specified handle is legitimate otherwise we can // assume the caller just passed in some bogus handle value // if (ObjectTableEntry != NULL) { // // From the object table entry we can grab a pointer to the object // header, get its type and its body // ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES); ObjectType = ObjectHeader->Type; Object = &ObjectHeader->Body; // // If the object type specifies an okay to close procedure then we // need to invoke that callback. If the callback doesn't want us to // close handle then unlock the object table and return the error // to our caller // if (ObjectType->TypeInfo.OkayToCloseProcedure != NULL) { if (!(*ObjectType->TypeInfo.OkayToCloseProcedure)( PsGetCurrentProcess(), Object, Handle )) { ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object ); ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry ); // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } Status = STATUS_HANDLE_NOT_CLOSABLE; leave; } } CapturedAttributes = ObjectTableEntry->ObAttributes; // // If the previous mode was user and the handle is protected from // being closed, then we'll either raise or return an error depending // on the global flags and debugger port situation. // if ((CapturedAttributes & OBJ_PROTECT_CLOSE) != 0) { if (KeGetPreviousMode() != KernelMode) { ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry ); if ((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) || (PsGetCurrentProcess()->DebugPort != NULL)) { // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } Status = KeRaiseUserException(STATUS_HANDLE_NOT_CLOSABLE); leave; } else { // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } Status = STATUS_HANDLE_NOT_CLOSABLE; leave; } } else { if ((!PsIsThreadTerminating(PsGetCurrentThread())) && (PsGetCurrentProcess()->Peb != NULL)) { ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry ); #if DBG // // bugcheck here on checked builds if kernel mode code is // closing a protected handle and process is not exiting. // Ignore if no PEB as this occurs if process is killed // before really starting. // KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 0, 0, 0); #else // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } Status = STATUS_HANDLE_NOT_CLOSABLE; leave; #endif // DBG } } } // // Get the granted access for the handle // #if defined(_X86_) && !FPO if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) { CapturedGrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex ); } else { CapturedGrantedAccess = ObjectTableEntry->GrantedAccess; } #else CapturedGrantedAccess = ObjectTableEntry->GrantedAccess; #endif // _X86_ && !FPO // // Now remove the handle from the handle table // ExDestroyHandle( ObjectTable, Handle, ObjectTableEntry ); // // perform any auditing required // // // Extract the value of the GenerateOnClose bit stored // after object open auditing is performed. This value // was stored by a call to ObSetGenerateOnClosed. // if (CapturedAttributes & OBJ_AUDIT_OBJECT_CLOSE) { if ( SepAdtAuditingEnabled ) { SeCloseObjectAuditAlarm( Object, (HANDLE)((ULONG_PTR)Handle & ~OBJ_HANDLE_TAGBITS), // Mask off the tagbits defined for OB objects. TRUE ); } } // // Since we took the handle away we need to decrement the objects // handle count, and remove a reference // ObpDecrementHandleCount( PsGetCurrentProcess(), ObjectHeader, ObjectHeader->Type, CapturedGrantedAccess ); ObDereferenceObject( Object ); ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object ); // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } // // And return to our caller // Status = STATUS_SUCCESS; leave; } else { // // At this point the input handle did not translate to a valid // object table entry // ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObpTypeObjectType, Handle ); // // If we are attached to the system process then return // back to our caller // if (AttachedToProcess) { KeUnstackDetachProcess(&ApcState); AttachedToProcess = FALSE; } // // Now if the handle is not null and it does not represent the // current thread or process then if we're user mode we either raise // or return an error // if ((Handle != NULL) && (Handle != NtCurrentThread()) && (Handle != NtCurrentProcess())) { if (KeGetPreviousMode() != KernelMode) { if ((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) || (PsGetCurrentProcess()->DebugPort != NULL)) { Status = KeRaiseUserException(STATUS_INVALID_HANDLE); leave; } else { Status = STATUS_INVALID_HANDLE; leave; } } else { // // bugcheck here if kernel debugger is enabled and if kernel mode code is // closing a bogus handle and process is not exiting. Ignore // if no PEB as this occurs if process is killed before // really starting. // if (( !PsIsThreadTerminating(PsGetCurrentThread())) && (PsGetCurrentProcess()->Peb != NULL)) { if (KdDebuggerEnabled) { KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 1, 0, 0); } } } } Status = STATUS_INVALID_HANDLE; leave; } } finally { KeLeaveCriticalRegion(); } return Status; }