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

转载来源:https://blog.csdn.net/wzsy/article/details/6188554

为对象分配内存看完了, 这次我们看一个比较高层的函数。
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值