郁金香外挂技术-郁金香灬老师

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 12|回复: 0

通过句柄找内核对象 ObReferenceObjectByHandle

[复制链接]
发表于 前天 17:50 | 显示全部楼层 |阅读模式
本帖最后由 郁金香灬老师 于 2021-11-25 17:51 编辑


NTSTATUS 获取句柄对象(IN HANDLE Handle)
{
        PKEVENT      userEvent=NULL;
        NTSTATUS status = ObReferenceObjectByHandle(
                Handle,
                EVENT_ALL_ACCESS,
                *PsProcessType,
                UserMode,
                (PVOID*)&userEvent,
                NULL);

        if(NT_SUCCESS(status))
        {
                // do something interesting here
                KeSetEvent(userEvent, IO_NO_INCREMENT, FALSE);
                ObDereferenceObject(userEvent);
                KdPrint(("yjx:SYS userEvent=%p status=%X Handle=%p\n", userEvent, status, Handle));
        }

        KdPrint(("yjx:SYS userEvent=%p status=%X\n", userEvent, status));

        return status;
}

//int ReadProcessMemoryForPid(UINT32 dwPid, PVOID pBase, PVOID lpBuffer, UINT32 nSize);
NTSTATUS IRP_IO_通过句柄获取对象(PIRP pirp)
{
//        PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(pirp); //获取应用层传来的参数
        UINT64* 缓冲区 = (UINT64*)(pirp->AssociatedIrp.SystemBuffer);
        KdPrint(("yjx:%s 行号%d\n", __FUNCDNAME__, __LINE__));
        NTSTATUS        status = STATUS_SUCCESS;
        if (缓冲区)
        {
                HANDLE handle = (HANDLE)(UINT64)缓冲区[0]; //传入数据
                status =获取句柄对象(handle);
                缓冲区[0] = status;
                pirp->IoStatus.Status = STATUS_SUCCESS;
                pirp->IoStatus.Information = 4;//返回给DeviceIoControl中的 倒数第二个参数lpBytesReturned
                IoCompleteRequest(pirp, IO_NO_INCREMENT);//调用方已完成所有I/O请求处理操作 并且不增加优先级
                 
        }
        //irpStack;
        return status;
}



一、句柄结构体
typedef struct _HANDLE_TABLE_ENTRY {
        union {
                PVOID Object;                          // 指向句柄所代表的对象
                ULONG ObAttributes;                    // 最低三位有特别含义,参见
                                                       // OBJ_HANDLE_ATTRIBUTES 宏定义
                PHANDLE_TABLE_ENTRY_INFO InfoTable;    // 各个句柄表页面的第一个表项
                                                       // 使用此成员指向一张表
                ULONG_PTR Value;
        };
        union {
                union {
                        ACCESS_MASK GrantedAccess;         // 访问掩码
                        struct {                           // 当NtGlobalFlag 中包含
                                                           // FLG_KERNEL_STACK_TRACE_DB 标记时使用
                                USHORT GrantedAccessIndex;
                                USHORT CreatorBackTraceIndex;
                        };
                };
                LONG NextFreeTableEntry;               // 空闲时表示下一个空闲句柄索引
        };
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
低32位:

Object & ~7 是句柄对应的内核对象的地址,Object 的低3位有特殊用途:第0位 OBJ_PROTECT_CLOSE 表示是否允许关闭句柄,但通过宏 ObpEncodeProtectClose 和 ObpGetHandleAttributes 将此位转移到 GrantedAccess 成员中,然后第0位被用作表示句柄表项锁,置1表示此句柄表项被锁住;第1位 OBJ_INHERIT 表示是否拷贝父进程的句柄表;第2位 OBJ_AUDIT_OBJECT_CLOSE 表示关闭该对象时是否产生审计事件。

高32位:

当句柄不指向内核对象时,NextFreeTableEntry 表示句柄表中下一个空闲句柄位置,也可以理解为该进程创建新句柄时的句柄值;当句柄指向内核对象时,GrantedAccess 表示该句柄的访问掩码。

上一篇博客研究句柄表初始化时,已经分析过 ExpAllocateHandleTable 函数,最低层句柄表第0项是特殊项,其 NextFreeTableEntry 成员的值是 EX_ADDITIONAL_INFO_SIGNATURE,即−2,而在wrk源码中,第0项的 InfoTable 成员初始化为0(书上说第0项的 InfoTable 指向 HANDLE_TABLE_ENTRY_INFO,我没找到依据).

二、通过句柄找内核对象
此部分内容来自书上P133页,书上内容有错,我已纠正。

有效句柄有四种情况:

-1,表示当前进程
-2,表示当前线程
负值,其绝对值为 System 进程的句柄表(ObpKernelHandleTable)中的索引
不超过 226 的正值,表示在进程句柄表中的索引
解释下第四种(书上错印成226),为什么是2的26次方?因为一个进程最多只能有 224 个句柄,而句柄值需要乘以4,所以最大的句柄值就是 226 了。

解析句柄的函数是 ObReferenceObjectByHandle ,它可能会调用我在上一篇博客分析过的 ExpLookupHandleTableEntry 函数。

ObReferenceObjectByHandle
NTSTATUS
ObReferenceObjectByHandle (
    __in HANDLE Handle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_TYPE ObjectType,
    __in KPROCESSOR_MODE AccessMode,
    __out PVOID *Object,
    __out_opt POBJECT_HANDLE_INFORMATION HandleInformation
    )

/*++

Routine Description:

    Given a handle to an object this routine returns a pointer
    to the body of the object with proper ref counts
    通过句柄找内核对象

Arguments:

    Handle - Supplies a handle to the object being referenced.  It can
        also be the result of NtCurrentProcess or NtCurrentThread
    句柄,用于查找内核对象

    DesiredAccess - Supplies the access being requested by the caller
    调用者指定的内核对象访问权限

    ObjectType - Optionally supplies the type of the object we
        are expecting
    可选地指定内核对象类型

    AccessMode - Supplies the processor mode of the access
    内核/用户

    Object - Receives a pointer to the object body if the operation
        is successful
    内核对象输出参数

    HandleInformation - Optionally receives information regarding the
        input handle.

Return Value:

    An appropriate NTSTATUS value

--*/

{
    ACCESS_MASK GrantedAccess;
    PHANDLE_TABLE HandleTable;
    POBJECT_HEADER ObjectHeader;
    PHANDLE_TABLE_ENTRY ObjectTableEntry;
    PEPROCESS Process;
    NTSTATUS Status;
    PETHREAD Thread;

    ObpValidateIrql("ObReferenceObjectByHandle");

    Thread = PsGetCurrentThread ();
    *Object = NULL;

    //
    // Check is this handle is a kernel handle or one of the two builtin pseudo handles
    // 检查是不是-1或-2
    //
    if ((LONG)(ULONG_PTR) Handle < 0) {
        //
        //  If the handle is equal to the current process handle and the object
        //  type is NULL or type process, then attempt to translate a handle to
        //  the current process. Otherwise, check if the handle is the current
        //  thread handle.
        //

        if (Handle == NtCurrentProcess()) {
            // 如果句柄值是-1,即当前进程

            // 检查类型是否合法
            if ((ObjectType == PsProcessType) || (ObjectType == NULL)) {               

                // 获取当前进程内核对象
                Process = PsGetCurrentProcessByThread(Thread);
                GrantedAccess = Process->GrantedAccess;

                // SeComputeDeniedAccesses 检查权限,返回0表示权限检查通过;
                // 如果是内核调用,则不需要检查权限
                if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
                    (AccessMode == KernelMode)) {

                    // 获取对象头
                    ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);

                    // 如果指定了输出参数 HandleInformation ,就填入一些信息
                    if (ARGUMENT_PRESENT(HandleInformation)) {

                        HandleInformation->GrantedAccess = GrantedAccess;
                        HandleInformation->HandleAttributes = 0;
                    }

                    // 指针计数加一
                    ObpIncrPointerCount(ObjectHeader);

                    // 返回进程内核对象
                    *Object = Process;

                    ASSERT( *Object != NULL );

                    Status = STATUS_SUCCESS;

                } else {

                    Status = STATUS_ACCESS_DENIED;
                }

            } else {

                Status = STATUS_OBJECT_TYPE_MISMATCH;
            }

            return Status;

        //
        //  If the handle is equal to the current thread handle and the object
        //  type is NULL or type thread, then attempt to translate a handle to
        //  the current thread. Otherwise, the we'll try and translate the
        //  handle
        //

        } else if (Handle == NtCurrentThread()) {
            // 如果句柄值是-2,即当前线程,和进程的做法类似
            if ((ObjectType == PsThreadType) || (ObjectType == NULL)) {

                GrantedAccess = Thread->GrantedAccess;

                if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
                    (AccessMode == KernelMode)) {

                    ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);

                    if (ARGUMENT_PRESENT(HandleInformation)) {

                        HandleInformation->GrantedAccess = GrantedAccess;
                        HandleInformation->HandleAttributes = 0;
                    }

                    ObpIncrPointerCount(ObjectHeader);
                    *Object = Thread;

                    ASSERT( *Object != NULL );

                    Status = STATUS_SUCCESS;

                } else {

                    Status = STATUS_ACCESS_DENIED;
                }

            } else {

                Status = STATUS_OBJECT_TYPE_MISMATCH;
            }

            return Status;

        } else if (AccessMode == KernelMode) {
            //
            //  Make the handle look like a regular handle
            //

            // 如果句柄是负数且是内核模式,就取句柄绝对值
            Handle = DecodeKernelHandle( Handle );

            //
            //  The global kernel handle table
            // 句柄值是负数,已经改成其绝对值了,从内核句柄表 ObpKernelHandleTable 里找

            HandleTable = ObpKernelHandleTable;
        } else {
            //
            // The previous mode was user for this kernel handle value. Reject it here.
            //

            // 用户模式不能访问系统进程句柄,返回非法句柄错误
            return STATUS_INVALID_HANDLE;
        }

    } else {
        // 获取当前进程的句柄表
        HandleTable = PsGetCurrentProcessByThread(Thread)->ObjectTable;
    }

    // 此时 HandleTable 可能是普通进程句柄表,也可能是系统进程句柄表(ObpKernelHandleTable)

    ASSERT(HandleTable != NULL);

    //
    // Protect this thread from being suspended while we hold the handle table entry lock
    // 加锁的目的是防止我们操作句柄表时线程被挂起
    //

    KeEnterCriticalRegionThread(&Thread->Tcb);

    //
    //  Translate the specified handle to an object table index.
    // 通过句柄表找句柄表项,ExMapHandleToPointerEx 底层调用了 ExpLookupHandleTableEntry
    //

    ObjectTableEntry = ExMapHandleToPointerEx ( HandleTable, Handle, AccessMode );

    //
    //  Make sure the object table entry really does exist
    // 确保句柄表项确实存在

    if (ObjectTableEntry != NULL) {

        // 获取内核对象头
        ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);

        //
        // Optimize for a successful reference by bringing the object header
        // into the cache exclusive.
        // 优化:将内核对象弄到 cache 里,在wrk里,这个宏没啥用,但在后续windows里是一种优化手段
        // 参考 _m_prefetch

        ReadForWriteAccess(ObjectHeader);

        // 检查类型是否合法
        if ((ObjectHeader->Type == ObjectType) || (ObjectType == NULL)) {

            // NtGlobalFlag 和调试有关,我不懂,2020年12月8日23:32:49
#if i386
            if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {

                GrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );

            } else {

                GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
            }
#else
            GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);

#endif // i386

            // SeComputeDeniedAccesses 返回0表示权限检查通过
            if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
                (AccessMode == KernelMode)) {

                PHANDLE_TABLE_ENTRY_INFO ObjectInfo;

                ObjectInfo = ExGetHandleInfo(HandleTable, Handle, TRUE);

                //
                //  Access to the object is allowed. Return the handle
                //  information is requested, increment the object
                //  pointer count, unlock the handle table and return
                //  a success status.
                //
                // 允许访问内核对象。如果需要,返回句柄信息。增加指针引用计数,
                // 解锁句柄表并返回成功。
                //
                //  Note that this is the only successful return path
                //  out of this routine if the user did not specify
                //  the current process or current thread in the input
                //  handle.
                //
                // 除了获取当前进程线程以外,这是唯一的成功返回执行路径。
                //

                // 可选地返回句柄信息
                if (ARGUMENT_PRESENT(HandleInformation)) {

                    HandleInformation->GrantedAccess = GrantedAccess;
                    HandleInformation->HandleAttributes = ObpGetHandleAttributes(ObjectTableEntry);
                }

                //
                //  If this object was audited when it was opened, it may
                //  be necessary to generate an audit now.  Check the audit
                //  mask that was saved when the handle was created.
                //
                // 如果内核对象审计选项开启,可能需要生成审计。检查句柄创建时保存的审计掩码
                //
                //  It is safe to do this check in a non-atomic fashion,
                //  because bits will never be added to this mask once it is
                //  created.
                //
                // 这里无须原子操作,因为掩码从初始化后就不变了
                //

                if ( (ObjectTableEntry->ObAttributes & OBJ_AUDIT_OBJECT_CLOSE) &&
                     (ObjectInfo != NULL) &&
                     (ObjectInfo->AuditMask != 0) &&
                     (DesiredAccess != 0)) {


                      ObpAuditObjectAccess( Handle, ObjectInfo, &ObjectHeader->Type->Name, DesiredAccess );
                }

                ObpIncrPointerCount(ObjectHeader);

                ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );

                KeLeaveCriticalRegionThread(&Thread->Tcb);

                *Object = &ObjectHeader->Body;

                ASSERT( *Object != NULL );

                return STATUS_SUCCESS;

            } else {

                Status = STATUS_ACCESS_DENIED;
            }

        } else {

            Status = STATUS_OBJECT_TYPE_MISMATCH;
        }

        ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );

    } else {

        Status = STATUS_INVALID_HANDLE;
    }

    KeLeaveCriticalRegionThread(&Thread->Tcb);


    return Status;
}
三、插入句柄表
将一个对象插入到句柄表中的函数是ObInsertObject,其代码位于base\ntos\ob
obinsert.c 文件中。它首先对参数做各种检查,然后调用ObpCreateHandle 函数,为要插入
的对象创建一个句柄。ObpCreateHandle 函数又调用ExCreateHandle 来创建一个句柄表项,
并填充新建的表项。这些函数的代码比较直截了当,这里不再赘述。

郁金香外挂教程,学习中...
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|郁金香外挂技术-郁金香灬老师 ( 苏ICP备10059359号 )

GMT+8, 2021-11-27 08:01 , Processed in 0.050225 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表