三。对象创建(ObCreateObject)和对象删除(ObDereferenceObject、ObpRemoveObjectRoutine)

三。对象创建(ObCreateObject)和对象删除(ObDereferenceObject、ObpRemoveObjectRoutine)

2010年05月11日 星期二 11:20

为对象分配内存看完了, 这次我们看一个比较高层的函数。
ObCreateObject, 这是内核的导出函数, 所有模块都可以使用(虽然它没有被文档化....)
它的作用是创建指定类型(OBJECT_TYPE)的对象示例。(注意ObAllocateObject只是分配了空间, 这里可以看到后续的操作)
这个函数的参数还真多啊。微软函数的参数一直都和火车一样。我们只能淡定。。。
NTSTATUS
ObCreateObject (
    __in KPROCESSOR_MODE ProbeMode,            // 决定是否要验证参数
    __in POBJECT_TYPE ObjectType,              // 对象类型指针
    __in POBJECT_ATTRIBUTES ObjectAttributes,  // 对象的属性, 最终会转化成ObAllocateObject需要的OBJECT_CREATE_INFORMATION结构
    __in KPROCESSOR_MODE OwnershipMode,        // 内核对象?用户对象? 同上
    __inout_opt PVOID ParseContext,            // 这参数没用
    __in ULONG ObjectBodySize,                 // 对象体大小
    __in ULONG PagedPoolCharge,                // ...
    __in ULONG NonPagedPoolCharge,             // ...
    __out PVOID *Object                        // 接收对象体的指针
    ) {
    ......
    
    // 由于ObAllocateObject是通过OBJECT_CREATE_INFORMATION结构控制生成对象的, 这里先生成一个这个结构
    ObjectCreateInfo = ObpAllocateObjectCreateInfoBuffer();
    if (ObjectCreateInfo == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;    // 出错就退出了
    } else {    
    
    // 这个函数填写OBJECT_CREATE_INFORMATION结构, 里面只是一堆转换操作
        Status = ObpCaptureObjectCreateInformation( ObjectType,
                                                    ProbeMode,
                                                    OwnershipMode,
                                                    ObjectAttributes,
                                                    &CapturedObjectName,
                                                    ObjectCreateInfo,
                                                    FALSE );
       if (NT_SUCCESS(Status)) {
       
            // OBJECT_TYPE.TypeInfo.InvalidAttributes 包含了本类对象的非法属性
            // 如果和用户请求的属性有冲突就退出
            if (ObjectType->TypeInfo.InvalidAttributes & ObjectCreateInfo->Attributes) {
                Status = STATUS_INVALID_PARAMETER;
            } else {

                // 没有冲突, 填写Charge
                if (PagedPoolCharge == 0) {
                    PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
                }
                if (NonPagedPoolCharge == 0) {
                    NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
                }
                ObjectCreateInfo->PagedPoolCharge = PagedPoolCharge;
                ObjectCreateInfo->NonPagedPoolCharge = NonPagedPoolCharge;

                // 生成对象体, 已经看过这个函数了
                Status = ObpAllocateObject( ObjectCreateInfo,
                                            OwnershipMode,
                                            ObjectType,
                                            &CapturedObjectName,
                                            ObjectBodySize,
                                            &ObjectHeader );

                if (NT_SUCCESS(Status)) {
                    // 生成成功检测权限
                    *Object = &ObjectHeader->Body;
                    if (ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT) {
                        if (!SeSinglePrivilegeCheck( SeCreatePermanentPrivilege,
                                                     ProbeMode)) {
                            ObpFreeObject(*Object);
                            Status = STATUS_PRIVILEGE_NOT_HELD;
                        }
                    }
                    // 成功退出
                    return Status;
                }
            }

//一下都是错误处理

            ObpReleaseObjectCreateInformation(ObjectCreateInfo);
            if (CapturedObjectName.Buffer != NULL) {
                ObpFreeObjectNameBuffer(&CapturedObjectName);
            }
        }
        ObpFreeObjectCreateInfoBuffer(ObjectCreateInfo);
    }  
    return Status;
}

这个函数做的事情非常简单
1。 使用ObpCaptureObjectCreateInformation函数把用户传进来的参数转化为OBJECT_CREATE_INFORMATION结构
    因为使用为对象ObAllocateObject非配内存时, 这个结构控制了非配的行为
2。 ObpAllocateObject为对象非配内存
3。 检测一下权限什么, 就完了

有创建就有删除, windows中对对象的引用包含两种, 使用指针和使用句柄。
这两种方式在OBJECT_HEADER中分别有两个域用作计数, 当这两个计数都到达0时, 对象被删除了。
kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int4B   指针计数
   +0x004 HandleCount      : Int4B   句柄计数
   .......

句柄的操作要涉及到进程的句柄表, 比较麻烦。先看看指针计数。
下面这个函数负责减少指针计数, 如果计数到达0就删除对象。这个函数的实现非常简单
LONG_PTR
FASTCALL
ObfDereferenceObject (
    __in PVOID Object       //对象体指针
    ) {
    ......
    
    // 直接在对象体的基础上减去 0x18就是对象头了
    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );

    // 获得Type指针
    ObjectType = ReadForWriteAccess(&ObjectHeader->Type);

    // 简单的递减 PointerCount 域
    Result = ObpDecrPointerCount( ObjectHeader );

    // 递减到0就要删除啦
    if (Result == 0) {
        OldIrql = KeGetCurrentIrql();

        ASSERT(ObjectHeader->HandleCount == 0);

        if ( !KeAreAllApcsDisabled() ) {
            // 如果在PASSIVE Level调用ObpRemoveObjectRoutine删除
            ObpRemoveObjectRoutine( Object, FALSE );
            return Result;
        } else {
            // 否则调用 ObpDeferObjectDeletion 延迟删除
            ObpDeferObjectDeletion (ObjectHeader);
        }
    }

    return Result;
}

ObpDeferObjectDeletion和ObpRemoveObjectRoutine的目的都是一样的。
只不过在中断级比较高时ObpDeferObjectDeletion通过把请求派遣到系统工作线程中来降低优先级。
它的调用路径是
ObpDeferObjectDeletion->ExQueueWorkItem->(工作线程中)ObpProcessRemoveObjectQueue->ObpRemoveObjectRoutine
所以它最终还是调用ObpRemoveObjectRoutine来执行删除操作。

来看看对象的删除
VOID
ObpRemoveObjectRoutine (
    IN  PVOID   Object,                 // 对象体
    IN  BOOLEAN CalledOnWorkerThread    // 是否是工作线程调用
    ) {
    
    // 获得OBJECT_HEADER、对象类型、CreatorInfo和NameInfo
    // 若CreatorInfo和NameInfo不存在则返回NULL
    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    ObjectType = ObjectHeader->Type;
    CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO( ObjectHeader );
    NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );    
    
    // 如果CreatorInfo存在, 并且它在对象链表中, 摘链
    if (CreatorInfo != NULL && !IsListEmpty( &CreatorInfo->TypeList )) {
        ObpEnterObjectTypeMutex( ObjectType );
        RemoveEntryList( &CreatorInfo->TypeList );
        ObpLeaveObjectTypeMutex( ObjectType );
    }
    
    // 释放名字占用的空间, 还记得ObAllocateObject中对Name的直接赋值吗? 这里进行了释放
    if (NameInfo != NULL && NameInfo->Name.Buffer != NULL) {
        ExFreePool( NameInfo->Name.Buffer );
        NameInfo->Name.Buffer = NULL;
        NameInfo->Name.Length = 0;
        NameInfo->Name.MaximumLength = 0;
    }
    
    // 权限检查略过
    if (ObjectHeader->SecurityDescriptor != NULL) {
        ......
    }
    
    // 使用对象类型中的_OBJECT_TYPE_INITIALIZER结构中的DeleteProcedure删除对象
    if (ObjectType->TypeInfo.DeleteProcedure) {
        ObpBeginTypeSpecificCallOut( SaveIrql );
        if (!CalledOnWorkerThread) {
            ObjectHeader->Flags |= OB_FLAG_DELETED_INLINE;
        }
        (*(ObjectType->TypeInfo.DeleteProcedure))(Object);
        ObpEndTypeSpecificCallOut( SaveIrql, "Delete", ObjectType, Object );
    }
    
    // 这个是ObAllocateObject的逆操作
    ObpFreeObject( Object );
}

比较重要的是_OBJECT_TYPE_INITIALIZER结构, 在ObInitSystem()中的创建类型对象部分我们已经看到过。它包含了众多回调函数地址。
由于windows的对象是使用统一的函数管理的, 他们的接口都一样。但相同操作不同对象应该做的事情肯定是不一样的。
像打开文件和打开注册表就是完全不同的操作。
于是windows将类型与类型之间行为不同的操作放入_OBJECT_TYPE_INITIALIZER结构中。这样就可以使用相同的接口来操作了。
比如上面的ObpRemoveObjectRoutine就调用了其中的DeleteProcedure路径。
ObpRemoveObjectRoutine并不需要知道这个对象是什么, 它只知道要删除一个对象应该调用对应_OBJECT_TYPE_INITIALIZER中的DeleteProcedure函数就可以了。
如果系统中加入了新类型ObpRemoveObjectRoutine的代码完全不用修改。做到使用统一的接口管理不同的对象。

最后展示一下_OBJECT_TYPE_INITIALIZER
kd> dt _OBJECT_TYPE_INITIALIZER
nt!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : Uint2B
   +0x002 UseDefaultObject : UChar
   +0x003 CaseInsensitive  : UChar
   +0x004 InvalidAttributes : Uint4B            该类型的非法参数
   +0x008 GenericMapping   : _GENERIC_MAPPING
   +0x018 ValidAccessMask  : Uint4B
   +0x01c SecurityRequired : UChar
   +0x01d MaintainHandleCount : UChar
   +0x01e MaintainTypeList : UChar
   +0x020 PoolType         : _POOL_TYPE
   +0x024 DefaultPagedPoolCharge : Uint4B
   +0x028 DefaultNonPagedPoolCharge : Uint4B
   +0x02c DumpProcedure    : Ptr32     void      以下就是不同功能的回调函数了
   +0x030 OpenProcedure    : Ptr32     long 
   +0x034 CloseProcedure   : Ptr32     void 
   +0x038 DeleteProcedure  : Ptr32     void 
   +0x03c ParseProcedure   : Ptr32     long 
   +0x040 SecurityProcedure : Ptr32     long 
   +0x044 QueryNameProcedure : Ptr32     long 
   +0x048 OkayToCloseProcedure : Ptr32     unsigned char 

猜你喜欢

转载自blog.csdn.net/qq1841370452/article/details/81634450