前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >x64windows内核句柄表PspCidTable原理与解析

x64windows内核句柄表PspCidTable原理与解析

作者头像
IBinary
发布2023-07-24 21:52:58
6300
发布2023-07-24 21:52:58
举报
文章被收录于专栏:逆向技术逆向技术

全局句柄表详解

一丶句柄表

1.1 句柄表介绍

句柄表老生常谈的话题,里面存储了 进程线程的对象信息。 通过句柄表也可以遍历出隐藏的进程。也就是说全局句柄表里面存储的并不是句柄 而是进程EPROCESS 和线程 ETHREAD

1.2 定位句柄表

在内核中有一个变量,变量叫做 PspCidTable这个变量是未导出的变量,所以我们要使用特征码搜索。 自己定位一下(PsLookUpProcessByProcessId此函数就可以)

这个变量记录的就是句柄表 _HANDLE_TABLE 结构

windbg查看变量如下:

代码语言:javascript
复制
3: kd> dp PspCidTable
fffff800`5a223518  ffff888f`880067c0 ffffe18f`a3edc290
fffff800`5a223528  ffffe18f`a3fc1e80 00000000`00000002
fffff800`5a223538  00000000`00000000 00001000`00010000
fffff800`5a223548  00000000`00000140 00007100`00010110
fffff800`5a223558  00000000`00000000 00000000`00000000
fffff800`5a223568  00000000`00000000 00000000`00000000
fffff800`5a223578  0000002e`00000000 00000000`00000000
fffff800`5a223588  00000000`00000000 ffffe18f`a3fef0c0

第一项就是 我们要找的 _HANDLE_TABLE

将变量解析为结构体类型

代码语言:javascript
复制
0: kd> dt _HANDLE_TABLE ffff888f`880067c0
nt!_HANDLE_TABLE
   +0x000 NextHandleNeedingPool : 0x2000
   +0x004 ExtraInfoPages   : 0n0
   +0x008 TableCode        : 0xffff888f`8b9fe001
   +0x010 QuotaProcess     : (null) 
   +0x018 HandleTableList  : _LIST_ENTRY [ 0xffff888f`880067d8 - 0xffff888f`880067d8 ]
   +0x028 UniqueProcessId  : 0
   +0x02c Flags            : 1
   +0x02c StrictFIFO       : 0y1
   +0x02c EnableHandleExceptions : 0y0
   +0x02c Rundown          : 0y0
   +0x02c Duplicated       : 0y0
   +0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 HandleTableLock  : _EX_PUSH_LOCK
   +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
   +0x040 ActualEntry      : [32]  ""
   +0x060 DebugInfo        : (null) 

其中在这个表中最终要的成员就是 TableCode 他才是真正的 句柄信息表 记录着所有的句柄信息。 你可以理解为 这个成员就是一个数组的首地址。而里面的内容就是数组的成员项。

其中 _HANDLE_TABLE 定义如下:(各版本平台可能不一,请用Windbg自己DT查看一下)

代码语言:javascript
复制
//0x80 bytes (sizeof)
struct _HANDLE_TABLE
{
    ULONG NextHandleNeedingPool;                                            //0x0
    LONG ExtraInfoPages;                                                    //0x4
    volatile ULONG TableCode;        //全局对象数组首地址                                       //0x8
    struct _EPROCESS* QuotaProcess;                                         //0xc
    struct _LIST_ENTRY HandleTableList;                                     //0x10
    ULONG UniqueProcessId;                                                  //0x18
    union
    {
        ULONG Flags;                                                        //0x1c
        struct
        {
            UCHAR StrictFIFO:1;                                             //0x1c
            UCHAR EnableHandleExceptions:1;                                 //0x1c
            UCHAR Rundown:1;                                                //0x1c
            UCHAR Duplicated:1;                                             //0x1c
            UCHAR RaiseUMExceptionOnInvalidHandleClose:1;                   //0x1c
        };
    };
    struct _EX_PUSH_LOCK HandleContentionEvent;                             //0x20
    struct _EX_PUSH_LOCK HandleTableLock;                                   //0x24
    union
    {
        struct _HANDLE_TABLE_FREE_LIST FreeLists[1];                        //0x40
        struct
        {
            UCHAR ActualEntry[20];                                          //0x40
            struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo;                     //0x54
        };
    };
}; 

可以自己逆向也可以通过下方链接查看。

Vergilius Project | _HANDLE_TABLE

1.3 三层结构介绍

1.3.1 TableCode 详解

windows规定了 TableCode低两位是代表当前句柄表的层数,最多有三层,初始化默认就是一层,如果句柄不断增多那么就会有三层。

而最后一个掩码代表了当前是几层表结构. 0 1 2 分别代表了 1层结构 2层结构 三层结构

以上述地址为例(TableCode)

0xffff888f8b9fe001 后缀为1 那么代表它是一个两个结构.

一层结构

如果 TableCode & 3 = 0 那么就代表是一个一层表结构,简单理解为就是一个一维数组。 TableCode(抹掉低两位) 形成的指针 直接指向的就是 句柄表存储结构(_HANDLE_TABLE_ENRY)

而windows规定了一页4KB4096)个字节来存储这些信息。如果超过了就放入第二个一维数组中。所以 4096 /sizeof(_HANDLE_TABLE_ENTRY)= 256. 那么如果是一级句柄表,那么它的数组里面只能存放256_HANDLE_TABLE_ENTRY 也就是说按照windows +4方式管理进程的话,那么一个表内只能存储 256 / 4 = 40个进程

64位下的 _HANDLE_TABLE_ENTRY 大小是16个字节。32位则是八个字节

二层结构

如果值为1 那么就是二层结构, 二层结构你就认为他也是数组,只不过数组里面存放的内容是 一层结构数组的指针。

既然存储的是一维数组的指针,那么在64位的系统下一个指针是八个字节,那么句柄表存储是按照一页来存储的。那么 4096 / 8 = 512. 所以说二维数组里面能存放512项一维数组指针。在换算一下存储的进程来说. 1层表能存储40个进程 那么二级表能存储512 * 40 大小的进程(20480),所以一般我们的系统会用到二级表结构.但是很少(或者基本不会)使用到三层表.

实战:

比如我们调试的地址为 : 0xffff888f8b9fe001 用此值 & 3 = 1 那么代表我们就是一个二层结构。 那么我们的地址 0xffff888f8b9fe001去掉后面1位补零` 就是数组的起始地址。

windbg如下:

代码语言:javascript
复制
0: kd> dp 0xffff888f`8b9fe000  注意这个地址我们去掉了后面
ffff888f`8b9fe000  ffff888f`8802a000 ffff888f`8b9ff000
ffff888f`8b9fe010  ffff888f`8c0ff000 ffff888f`8ca15000
ffff888f`8b9fe020  ffff888f`8cf33000 ffff888f`8d667000
ffff888f`8b9fe030  ffff888f`8de74000 ffff888f`8ec47000
ffff888f`8b9fe040  00000000`00000000 00000000`00000000
ffff888f`8b9fe050  00000000`00000000 00000000`00000000
ffff888f`8b9fe060  00000000`00000000 00000000`00000000
ffff888f`8b9fe070  00000000`00000000 00000000`00000000

可以看到里面共有八项,这八项每一项都是一个一维数组指针。 第一项里面则是存储了 句柄表存储信息结构。也就是_HANDLE_TABLE_ENTRY

图如下:

三层结构

三层结构和二层结构一样,三层结构的话那么代表当前的TableCode指向的是一个三维数组指针,它能存储512项二维数组指针,而二维数组指针能存储 512 项一维数组指针,一维里面则存放了 句柄表信息结构 _HANDLE_TABLE_ENTRY (512 * 512 * 4 的进程,基本用不到.)

1.4 _HANDLE_TABLE_ENTRY 信息

这个结构才是我们真正记录句柄信息的结构,也就是说如果我们的 TableCode 是1级表的话,那么里面的内容都是 _HANDLE_TABLE_ENTRY . 如果是2级表.那么存的只是1级数组的指针.

结构如下:

代码语言:javascript
复制
0: kd> dt _HANDLE_TABLE_ENTRY
nt!_HANDLE_TABLE_ENTRY
   +0x000 VolatileLowValue : Int8B
   +0x000 LowValue         : Int8B
   +0x000 InfoTable        : Ptr64 _HANDLE_TABLE_ENTRY_INFO
   +0x008 HighValue        : Int8B
   +0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
   +0x008 LeafHandleValue  : _EXHANDLE
   +0x000 RefCountField    : Int8B
   +0x000 Unlocked         : Pos 0, 1 Bit
   +0x000 RefCnt           : Pos 1, 16 Bits
   +0x000 Attributes       : Pos 17, 3 Bits
   +0x000 ObjectPointerBits : Pos 20, 44 Bits
   +0x008 GrantedAccessBits : Pos 0, 25 Bits
   +0x008 NoRightsUpgrade  : Pos 25, 1 Bit
   +0x008 Spare1           : Pos 26, 6 Bits
   +0x00c Spare2           : Uint4B

此结构其实由很多联合体构成,可以参考文档定义。

文档链接如下:

Vergilius Project | _HANDLE_TABLE_ENTRYVergilius Project | _HANDLE_TABLE_ENTRY

代码语言:javascript
复制
//0x8 bytes (sizeof)
union _HANDLE_TABLE_ENTRY
{
    volatile LONG VolatileLowValue;                                         //0x0
    LONG LowValue;                                                          //0x0
    struct
    {
        struct _HANDLE_TABLE_ENTRY_INFO* volatile InfoTable;                //0x0
    LONG HighValue;                                                         //0x4
    union _HANDLE_TABLE_ENTRY* NextFreeHandleEntry;                         //0x4
        struct _EXHANDLE LeafHandleValue;                                   //0x4
    };
    ULONG Unlocked:1;                                                       //0x0
    ULONG Attributes:2;                                                     //0x0
    struct
    {
        ULONG ObjectPointerBits:29;                                         //0x0
    LONG RefCountField;                                                     //0x4
    ULONG GrantedAccessBits:25;                                             //0x4
    ULONG ProtectFromClose:1;                                               //0x4
    ULONG NoRightsUpgrade:1;                                                //0x4
    };
    ULONG RefCnt:5;                                                         //0x4
}; 

其中这里面的 InfoTable定义的则是我们想要查看的对象信息。 这里还有一个很重要的成员. GrantedAccessBits 代表了当前句柄的权限.

windbg实战如下(接上面的windbg命令信息)

代码语言:javascript
复制
0: kd> dp ffff888f`8802a000  首先查看一维数组
ffff888f`8802a000  00000000`00000000 00000000`00000000
ffff888f`8802a010  e18fa3e9`5040ff47 00000000`00000000
ffff888f`8802a020  e18fa8ab`1080ffb9 00000000`00000000
ffff888f`8802a030  e18fa3f7`d080ffdf 00000000`00000000
ffff888f`8802a040  e18fa3f6`c080ffdf 00000000`00000000
ffff888f`8802a050  e18fa3ed`e080ffdf 00000000`00000000
ffff888f`8802a060  e18fa3f1`3080ffdf 00000000`00000000
ffff888f`8802a070  e18fa3fb`4140ffdf 00000000`00000000
0: kd> dt _HANDLE_TABLE_ENTRY ffff888f`8802a010   第一项没值,看第二项。(Pid=4)
nt!_HANDLE_TABLE_ENTRY
   +0x000 VolatileLowValue : 0n-2193354271036997817
   +0x000 LowValue         : 0n-2193354271036997817
   +0x000 InfoTable        : 0xe18fa3e9`5040ff47 _HANDLE_TABLE_ENTRY_INFO
   +0x008 HighValue        : 0n0
   +0x008 NextFreeHandleEntry : (null) 
   +0x008 LeafHandleValue  : _EXHANDLE
   +0x000 RefCountField    : 0n-2193354271036997817
   +0x000 Unlocked         : 0y1
   +0x000 RefCnt           : 0y0111111110100011 (0x7fa3)
   +0x000 Attributes       : 0y000
   +0x000 ObjectPointerBits : 0y11100001100011111010001111101001010100000100 (0xe18fa3e9504)
   +0x008 GrantedAccessBits : 0y0000000000000000000000000 (0)
   +0x008 NoRightsUpgrade  : 0y0
   +0x008 Spare1           : 0y000000 (0)
   +0x00c Spare2           : 0

查看

查看第二项得知 infoTable值为 0xe18fa3e95040ff47 也就是我们的对象信息.

此值是加密的我们需要解密。下面说如何解密

二丶句柄解密

2.1 Win10(64)版本解密

2.1.1 逆向 ExpLookupHandleTableEntry

此API根据句柄表和句柄值来查询我们想要的 _HANDLE_TABLE_ENTRY项的。 其实他本质就是根据三层结构来进行查表。

逆向结果如下:

代码语言:javascript
复制
unsigned __int64 __fastcall ExpLookupHandleTableEntry(_HANDLE_TABLE *a1, _EXHANDLE pExHandleHandle)
{
  unsigned __int64 tagBits; // rdx
  __int64 *TableCode; // r8

  tagBits = pExHandleHandle.Value & 0xFFFFFFFFFFFFFFFCui64;
  if ( tagBits >= a1->NextHandleNeedingPool )
    return 0i64;

  TableCode = (__int64 *)a1->TableCode;         // //Type Level
  if ( ((unsigned __int8)TableCode & 3) == 1 )
    return *(__int64 *)((char *)&TableCode[(tagBits >> 10) - 1] + 7) + 4 * (tagBits & 1023);

  if ( ((unsigned __int8)TableCode & 3) != 0 )
    return *(_QWORD *)(*(__int64 *)((char *)&TableCode[(tagBits >> 0x13) - 1] + 6) + 8 * ((tagBits >> 0xA) & 0x1FF))
         + 4 * (tagBits & 0x3FF);

  return (unsigned __int64)TableCode + 4 * tagBits;
}

看的不明白,下面是还原的代码。

代码语言:javascript
复制
typedef struct _EXHANDLE
{
    union
    {
        struct
        {
            ULONG TagBits : 2;
            ULONG Index : 30;
        };
        VOID *GenericHandleOverlay;
        ULONG Value;
    };
} EXHANDLE, *PEXHANDLE;

typedef struct _HANDLE_TABLE_ENTRY
{
    union
    {
        PVOID Object;
        ULONG ObAttributes;
        ULONG_PTR Value;
    };
    union
    {
        ACCESS_MASK GrantedAccess;
        LONG NextFreeTableEntry;
    };
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

// 0x80 bytes (sizeof)
typedef struct _HANDLE_TABLE
{
    ULONG NextHandleNeedingPool;
    LONG ExtraInfoPages;
    volatile ULONG TableCode;
} HANDLE_TABLE, *PHANDLE_TABLE;

PHANDLE_TABLE_ENTRY ExpLookupHandleTableEntry(
    IN PHANDLE_TABLE HandleTable,
    IN EXHANDLE Handle)

{
    ULONG_PTR i, j, k;
    ULONG_PTR CapturedTable;
    ULONG TableLevel;
    PHANDLE_TABLE_ENTRY Entry;
    const int LEVEL_CODE_MASK = 3;
    const int MIDLEVEL_THRESHOLD = 1024 * 512;
    const int LOWLEVEL_COUNT = 512;

    typedef HANDLE_TABLE_ENTRY *L1P;
    typedef volatile L1P *L2P;
    typedef volatile L2P *L3P;

    L1P TableLevel1;
    L2P TableLevel2;
    L3P TableLevel3;

    ULONG_PTR RemainingIndex;
    ULONG_PTR MaxHandle;
    ULONG_PTR Index;

    PAGED_CODE();
    Handle.TagBits = 0;
    Index = Handle.Index;

    MaxHandle = *(volatile ULONG *)&HandleTable->NextHandleNeedingPool;

    if (Handle.Value >= MaxHandle)
    {
        return NULL;
    }

    CapturedTable = *(volatile ULONG_PTR *)&HandleTable->TableCode;
    TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
    CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;

    switch (TableLevel)
    {

    case 0:

        TableLevel1 = (L1P)CapturedTable;
        Entry = &(TableLevel1[Index]);
        break;

    case 1:
    {

        TableLevel2 = (L2P)CapturedTable;

        i = Index / LOWLEVEL_COUNT; // Calc table
        j = Index % LOWLEVEL_COUNT; // calc table offset

        Entry = &(TableLevel2[i][j]);
    }

    break;

    case 2:
    {
        TableLevel3 = (L3P)CapturedTable;

        i = Index / (MIDLEVEL_THRESHOLD);
        RemainingIndex = Index - i * MIDLEVEL_THRESHOLD;
        j = RemainingIndex / LOWLEVEL_COUNT;
        k = RemainingIndex % LOWLEVEL_COUNT;
        Entry = &(TableLevel3[i][j][k]);
    }
    break;

    default:
        _assume(0);
    }

    return Entry;
}

使用:

代码语言:javascript
复制
  PHANDLE_TABLE pPspCidTable = (PHANDLE_TABLE)0xffff888f880067c0; //写死了自己特征码寻找
    HANDLE handle = (HANDLE)4;
    ExpLookupHandleTableEntry(pPspCidTable, *(PEXHANDLE)&handle);
    return STATUS_SUCCESS;

返回的则是我们想要的句柄项结构。

这是参考BlockBone库的实现 如果想要稳定可以使用此方法

代码语言:javascript
复制
PHANDLE_TABLE_ENTRY ExpLookupHandleTableEntry( IN PHANDLE_TABLE HandleTable, IN EXHANDLE tHandle )
{
    ULONG_PTR TableCode = HandleTable->TableCode & 3;
    if (tHandle.Value >= HandleTable->NextHandleNeedingPool)
        return NULL;

    tHandle.Value &= 0xFFFFFFFFFFFFFFFC;

#if defined ( _WIN10_ )
    if (TableCode != 0)
    {
        if (TableCode == 1)
        {
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(HandleTable->TableCode + 8 * (tHandle.Value >> 11) - 1) + 4 * (tHandle.Value & 0x7FC));
        }
        else
        {
            ULONG_PTR tmp = tHandle.Value >> 11;
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(*(ULONG_PTR*)(HandleTable->TableCode + 8 * (tHandle.Value >> 21) - 2) + 8 * (tmp & 0x3FF)) + 4 * (tHandle.Value & 0x7FC));
        }
    }
    else
    {
        return (PHANDLE_TABLE_ENTRY)(HandleTable->TableCode + 4 * tHandle.Value);
    }
#elif defined ( _WIN7_ )
    ULONG_PTR Diff = HandleTable->TableCode - TableCode;

    if (TableCode != 0)
    {
        if (TableCode == 1)
        {
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(Diff + ((tHandle.Value - tHandle.Value & 0x7FC) >> 9)) + 4 * (tHandle.Value & 0x7FC));
        }
        else
        {
            ULONG_PTR tmp = (tHandle.Value - tHandle.Value & 0x7FC) >> 9;
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(*(ULONG_PTR*)(Diff + ((tHandle.Value - tmp - tmp & 0xFFF) >> 10)) + (tmp & 0xFFF)) + 4 * (tHandle.Value & 0x7FC));
        }
    }
    else
    {
        return (PHANDLE_TABLE_ENTRY)(Diff + 4 * tHandle.Value);
    }
#else
    if (TableCode != 0)
    {
        if (TableCode == 1)
        {
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(HandleTable->TableCode + 8 * (tHandle.Value >> 10) - 1) + 4 * (tHandle.Value & 0x3FF));
        }
        else
        {
            ULONG_PTR tmp = tHandle.Value >> 10;
            return (PHANDLE_TABLE_ENTRY)(*(ULONG_PTR*)(*(ULONG_PTR*)(HandleTable->TableCode + 8 * (tHandle.Value >> 19) - 2) + 8 * (tmp & 0x1FF)) + 4 * (tHandle.Value & 0x3FF));
        }
    }
    else
    {
        return (PHANDLE_TABLE_ENTRY)(HandleTable->TableCode + 4 * tHandle.Value);
    }
#endif
}
2.1.2 解密 _HANDLE_TABLE_ENTRY

接1.4小结遗留问题,我们发现 _HANDLE_TABLE_ENTRY infoTable 是加密的,那么我们需要进行解密。如何解密其实这就是为什么要逆向2.1小节提供的API了。 因为windows系统内核也会使用它。只需要IDA 看看其交叉引用就知道了它是如何调用的,调用之后如何解密的。

随便查看一下交叉隐藏,下面就可以看到它的算法了。

算法如下:

代码语言:javascript
复制
(X >> 0X10)&0xFFFFFFFFFFFFFFF0ui64  == Object

那么根据我们 解析出的 InforTabe的值试试这个算法

代码语言:javascript
复制
(0xe18fa3e95040ff47 >> 0x10)&0xFFFFFFFFFFFFFFF0 == 0xFFFFE18FA3E95040

可以使用命令 !object 0xFFFFE18FA3E95040 查看是什么类型,在这里看到的是EPROCESS类型所以对其按照EPROCESS解析

代码语言:javascript
复制
0: kd> dt _EPROCESS 0XFFFFE18FA3E95040
nt!_EPROCESS
  ..........我已删除
  +0x450 ImageFileName    : [15]  "System"
  ......我已删除  

可以看到 一维数组中第一项的_HANDLE_TABLE_ENTRY 解密之后是 System进程

2.2 Win7(64)版本解密

Win764位下,获取到的值并不是加密的. 可以看到他判断了最后一位掩码. 所以说win7下的后值的后三位都是标志. 抹掉补零即可.

公式:

代码语言:javascript
复制
(X & FFFFFFFFFFFFFFF6) = object

以下是Windbg命令

代码语言:javascript
复制
0: kd> dp PspCidTable 
fffff800`04066988  fffff8a0`00004870 00000000`00000000
fffff800`04066998  ffffffff`80000020 00000000`00000101
fffff800`040669a8  ffffffff`80000308 ffffffff`80000024
fffff800`040669b8  00000000`00000000 00000000`00000113
fffff800`040669c8  00000000`00000000 00000000`00000000
fffff800`040669d8  fffff800`03fce650 00000000`00000000
fffff800`040669e8  00000000`00000000 00000000`00000000
fffff800`040669f8  00000000`00000000 00000000`00000008
0: kd> dt _HANDLE_TABLE fffff8a0`00004870
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xfffff8a0`00fdf001 //找到数组
   +0x008 QuotaProcess     : (null) 
   +0x010 UniqueProcessId  : (null) 
   +0x018 HandleLock       : _EX_PUSH_LOCK
   +0x020 HandleTableList  : _LIST_ENTRY [ 0xfffff8a0`00004890 - 0xfffff8a0`00004890 ]
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 DebugInfo        : (null) 
   +0x040 ExtraInfoPages   : 0n0
   +0x044 Flags            : 1
   +0x044 StrictFIFO       : 0y1
   +0x048 FirstFreeHandle  : 0xf70
   +0x050 LastFreeHandleEntry : 0xfffff8a0`02118bc0 _HANDLE_TABLE_ENTRY
   +0x058 HandleCount      : 0x308
   +0x05c NextHandleNeedingPool : 0x1000
   +0x060 HandleCountHighWatermark : 0x347
0: kd> dp 0xfffff8a0`00fdf000
fffff8a0`00fdf000  fffff8a0`00005000 fffff8a0`00fe0000
fffff8a0`00fdf010  fffff8a0`01593000 fffff8a0`02118000
fffff8a0`00fdf020  00000000`00000000 00000000`00000000
fffff8a0`00fdf030  00000000`00000000 00000000`00000000
fffff8a0`00fdf040  00000000`00000000 00000000`00000000
fffff8a0`00fdf050  00000000`00000000 00000000`00000000
fffff8a0`00fdf060  00000000`00000000 00000000`00000000
fffff8a0`00fdf070  00000000`00000000 00000000`00000000
0: kd> dp fffff8a0`00005000 //查看数组值
fffff8a0`00005000  00000000`00000000 000004cc`fffffffe
fffff8a0`00005010  fffffa80`18db0861 00000000`00000000
fffff8a0`00005020  fffffa80`18db02d1 00000000`00000000
fffff8a0`00005030  fffffa80`18e54041 00000000`00000000
fffff8a0`00005040  fffffa80`18dc7041 00000000`00000000
fffff8a0`00005050  fffffa80`18dc7b51 00000000`00000000
fffff8a0`00005060  fffffa80`18dc7661 fffffa80`00000000
fffff8a0`00005070  fffffa80`18dcfb51 fffffa80`00000000
0: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005000 //查看第一项
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : (null) //第一项为0并没有信息
   +0x000 ObAttributes     : 0
   +0x000 InfoTable        : (null) 
   +0x000 Value            : 0
   +0x008 GrantedAccess    : 0xfffffffe
   +0x008 GrantedAccessIndex : 0xfffe
   +0x00a CreatorBackTraceIndex : 0xffff
   +0x008 NextFreeTableEntry : 0xfffffffe
0: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005010 //然后查看第二项(中间加了0)
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : 0xfffffa80`18db0861 Void //定位到对象
   +0x000 ObAttributes     : 0x18db0861
   +0x000 InfoTable        : 0xfffffa80`18db0861 _HANDLE_TABLE_ENTRY_INFO
   +0x000 Value            : 0xfffffa80`18db0861
   +0x008 GrantedAccess    : 0
   +0x008 GrantedAccessIndex : 0
   +0x00a CreatorBackTraceIndex : 0
   +0x008 NextFreeTableEntry : 0
0: kd> !object 0xfffffa80`18db0860
Object: fffffa8018db0860  Type: (fffffa8018d3f910) Process
    ObjectHeader: fffffa8018db0830 (new version)
    HandleCount: 4  PointerCount: 159
0: kd> dt _EPROCESS fffffa8018db0860

nt!_EPROCESS
   .....
   +0x2e0 ImageFileName    : [15]  "System"
   .....

2.3 Win11(64)版本解密

逆向分析查看其交叉引用的位置,发现跟Win10一样

公式:

代码语言:javascript
复制
(X >> 0X10) & 0xFFFFFFFFFFFFFFF0i64;

三丶 _OBJECT_HEADER 结构

内核对象的每一个结构其开始位置都是 _OBJECT_HEADER 结构,我们就拿我们的EPROCESS来说。虽然我们解密出来直接使用 EPROCESS解析就能看到内容,但是其实它的头结构是

_OBJECT_HEADER

结构如下:

代码语言:javascript
复制
0: kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int8B
   +0x008 HandleCount      : Int8B
   +0x008 NextToFree       : Ptr64 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : UChar
   +0x019 TraceFlags       : UChar
   +0x019 DbgRefTrace      : Pos 0, 1 Bit
   +0x019 DbgTracePermanent : Pos 1, 1 Bit
   +0x01a InfoMask         : UChar
   +0x01b Flags            : UChar
   +0x01b NewObject        : Pos 0, 1 Bit
   +0x01b KernelObject     : Pos 1, 1 Bit
   +0x01b KernelOnlyAccess : Pos 2, 1 Bit
   +0x01b ExclusiveObject  : Pos 3, 1 Bit
   +0x01b PermanentObject  : Pos 4, 1 Bit
   +0x01b DefaultSecurityQuota : Pos 5, 1 Bit
   +0x01b SingleHandleEntry : Pos 6, 1 Bit
   +0x01b DeletedInline    : Pos 7, 1 Bit
   +0x01c Reserved         : Uint4B
   +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : Ptr64 Void
   +0x028 SecurityDescriptor : Ptr64 Void
   +0x030 Body             : _QUAD  //指向实际的对象结构

在我调试的电脑上结构占用是0x30字节。其中最后一项成员Body 才是真正的指向实际的内核对象的(比如EPROCESS)

所以如果我们想要查看实际的内核对象的OBJECT_HEADER结构,只需要将此对象结构 - sizeof(OBJECT_HEADER)结构即可。

拿上面的win10下的句柄对象,解密后的的EPROCESS地址举例,只需要-0x30即可看到实际的OBJECT_HEADER结构。

代码语言:javascript
复制
0: kd> dt _OBJECT_HEADER 0XFFFFE18FA3E95010
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n196681
   +0x008 HandleCount      : 0n5
   +0x008 NextToFree       : 0x00000000`00000005 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : 0x9a ''  重要点
   +0x019 TraceFlags       : 0 ''
   +0x019 DbgRefTrace      : 0y0
   +0x019 DbgTracePermanent : 0y0
   +0x01a InfoMask         : 0 ''
   +0x01b Flags            : 0x2 ''
   +0x01b NewObject        : 0y0
   +0x01b KernelObject     : 0y1
   +0x01b KernelOnlyAccess : 0y0
   +0x01b ExclusiveObject  : 0y0
   +0x01b PermanentObject  : 0y0
   +0x01b DefaultSecurityQuota : 0y0
   +0x01b SingleHandleEntry : 0y0
   +0x01b DeletedInline    : 0y0
   +0x01c Reserved         : 0
   +0x020 ObjectCreateInfo : 0xfffff800`5a11c200 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : 0xfffff800`5a11c200 Void
   +0x028 SecurityDescriptor : 0xffff888f`88006c6e Void
   +0x030 Body             : _QUAD

3.1 Win7下解密 Typeindex

win764位下并没有加密.直接就是最终结果.

代码语言:javascript
复制
0: kd> dt _OBJECT_HEADER FFFFFA8018DB0830
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n158
   +0x008 HandleCount      : 0n4
   +0x008 NextToFree       : 0x00000000`00000004 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : 0x7 '' //类型直接就是明文
   +0x019 TraceFlags       : 0 ''
   +0x01a InfoMask         : 0 ''
   +0x01b Flags            : 0x2 ''
   +0x020 ObjectCreateInfo : 0xfffff800`04043e40 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : 0xfffff800`04043e40 Void
   +0x028 SecurityDescriptor : 0xfffff8a0`00004592 Void
   +0x030 Body             : _QUAD

3.2 Win10下 解密 TypeIndex

TypeIndex代表的是句柄的类型,在每个系统都不一样,但是在这里其实也是加密的。 我们可以通过逆向未导出的API ObGetObjectType() 来进行分析,具体自己IDA分析一波。

解密方法是 为如下:

代码语言:javascript
复制
TypeIndex ^ ((OBJECT_HEADERADDR >> 8) & 0XFF ) ^ ObHeaderCookie

其中 typdeindex ((OBJECT_HEADERADDR >> 8) & 0XFF ) 我们都是已经知道的

((OBJECT_HEADERADDR >> 8) & 0XFF ) 其实本质就是取 OBJECT_HEADER的地址的倒数第二个字节。

例如上面的 0XFFFFE18FA3E95010 我们是取得 0XFFFFE18FA3E95010 这个字节。

ObHeaderCookie 是系统定义得它是一个字节。我们使用Windbg查看。

代码语言:javascript
复制
0: kd> db ObHeaderCookie
fffff800`5a22367c  cd 1c 93 fe 02 00 00 00-00 00 00 00 00 00 00 00  ................
fffff800`5a22368c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
fffff800`5a22369c  4f a3 58 03 01 00 00 00-00 00 00 00 00 00 00 00  O.X.............
fffff800`5a2236ac  00 00 00 00 20 e8 e5 a3-8f e1 ff ff 00 00 00 00  .... ...........
fffff800`5a2236bc  00 00 00 00 00 00 00 00-00 00 00 00 00 2d 31 01  .............-1.
fffff800`5a2236cc  00 00 00 00 e0 0a ec a3-8f e1 ff ff 30 03 e5 a3  ............0...
fffff800`5a2236dc  8f e1 ff ff 40 9c 00 00-00 00 00 00 00 00 00 00  ....@...........
fffff800`5a2236ec  00 10 00 00 40 cb 09 88-8f 88 ff ff eb ac b0 09  ....@...........

那么套用解密公式则是

代码语言:javascript
复制
0x9a ^ 0X50 ^ 0Xcd = 7

使用PChunter查看

3.3 win11下解密Typeindex

逆向 ObGetObjectType 发现跟win10一样.所以猜测 win11是一样来解密的

具体需要可以自己求证.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 全局句柄表详解
    • 一丶句柄表
      • 1.1 句柄表介绍
      • 1.2 定位句柄表
      • 1.3 三层结构介绍
      • 1.4 _HANDLE_TABLE_ENTRY 信息
    • 二丶句柄解密
      • 2.1 Win10(64)版本解密
      • 2.2 Win7(64)版本解密
      • 2.3 Win11(64)版本解密
    • 三丶 _OBJECT_HEADER 结构
      • 3.1 Win7下解密 Typeindex
      • 3.2 Win10下 解密 TypeIndex
      • 3.3 win11下解密Typeindex
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档