三。对象创建(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 
要发送IRP删除文件,可以使用以下步骤: 1. 打开文件对象:首先,需要打开要删除的文件对象。可以使用ZwCreateFile或NtOpenFile函数来打开文件。 2. 构建IRP:IRP(I/O Request Packet)是用于向内核发送I/O请求的数据结构。可以使用IoBuildSynchronousFsdRequest函数构建IRP。 3. 设置IRP参数:设置IRP的参数,包括请求类型、文件对象、I/O栈位置、缓冲区等。 4. 发送IRP:使用IoCallDriver函数发送IRP到驱动程序。 5. 处理IRP响应:等待驱动程序处理IRP请求,并处理IRP响应。如果删除文件成功,则可以关闭文件对象并返回成功信息。 下面是一个示例代码,演示如何使用IRP删除文件: NTSTATUS DeleteFile(PUNICODE_STRING FileName) { NTSTATUS status; HANDLE fileHandle; OBJECT_ATTRIBUTES objAttr; IO_STATUS_BLOCK ioStatus; PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PIRP irp; KEVENT event; InitializeObjectAttributes(&objAttr, FileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwCreateFile(&fileHandle, FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &objAttr, &ioStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0); if (!NT_SUCCESS(status)) { return status; } status = ObReferenceObjectByHandle(fileHandle, FILE_WRITE_ATTRIBUTES, *IoFileObjectType, KernelMode, (PVOID*)&fileObject, NULL); if (!NT_SUCCESS(status)) { ZwClose(fileHandle); return status; } deviceObject = IoGetRelatedDeviceObject(fileObject); irp = IoBuildSynchronousFsdRequest(IRP_MJ_SET_INFORMATION, deviceObject, NULL, 0, NULL, &event, &ioStatus); if (!irp) { ObDereferenceObject(fileObject); ZwClose(fileHandle); return STATUS_INSUFFICIENT_RESOURCES; } irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation; irpSp->Parameters.SetFile.DeleteFile = TRUE; KeInitializeEvent(&event, NotificationEvent, FALSE); status = IoCallDriver(deviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } ObDereferenceObject(fileObject); ZwClose(fileHandle); return status; } 这个函数接受一个UNICODE_STRING类型的文件名作为参数,并返回NTSTATUS类型的状态码。它使用ZwCreateFile函数打开文件,然后使用IoBuildSynchronousFsdRequest函数构建一个IRP,用于删除文件。最后,它使用IoCallDriver函数发送IRP,等待处理响应,并返回状态码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值