Ay's Blog@CNSSUESTC

windbg学习笔记 FOR 内核调试(三) --进程句柄表HANDLE_TABLE

                                      

想当年 初学核编 , 阅读第三章的内核对象的时候跟看天书没什么感觉 死命在想到底内核对象 , 句柄是个什么东西 干嘛用的 于是我们工作室的老大就对我说 这篇看过就过了 学到后面你自然会明白的
   
我想也是 , 很多时候感觉学东西的确是这样 暂时看不懂的先放着 过段时间再看回来就恍然大悟了 . 我前段时间又看了下核编的第三章 唯一的收获就是能够大概了解到 hanle 这个所谓的索引的作用了 . 我也跟工作室的小学弟讲过我理解的句柄 ( 但是讲完看到他们茫然的表情 ~ 郁闷了 ~ 没办法 可能知识面 实践经验等还不足以理解我说的东西吧 或者是被我们可爱的鼠仙传染了 也开始学着他讲天书了 ~)
   
以下讲的都是我所理解的东东 , 有错误的话也在所难免了
   
首先说说 HANDLE 这个是个什么东西 WinNT.h 里面查到其定义如下

typedef void *HANDLE;

~ 这下子明白了 原来 HANDLE 就是一个无类型指针 , 只是充当内核对象在进程句柄表里面的索引 , 相当于在进程句柄表内的一个 ID .

进程句柄表 个人理解就是存放这个进程所创建的内核对象在内核地址空间位置的一个表 ( 你可以理解为数组 ), 那么什么是内核对象呢 ? 内核对象只不过是系统资源的一种抽象 , 我新建了个进程 , 那么系统内部就会为这个进程分配资源 , 然后创建一个内核对象 , 返回一个句柄 通过这个句柄我们可以找到进程在内核空间的位置 , 其实也就是进程对应的内核对象的位置 , 在内核里面 , 内核对象就是一堆数据结构 , 只不过数据结构很大 , 里面存放着这个进程的信息 , 这样系统只要改变这个结构里面的数值就能操作进程 , 如此而已 ~

嘿嘿 消化一下上面说的 接下来就说进程句柄表了 , 进程句柄表是保存在进程的内核对象里面的 进程的内核对象就是个 EPROCESS 的结构 结构原型如下 :

kd> dt _EPROCESS

ntdll!_EPROCESS

   +0x000 Pcb              : _KPROCESS

   +0x06c ProcessLock      : _EX_PUSH_LOCK

   +0x070 CreateTime       : _LARGE_INTEGER

   +0x078 ExitTime         : _LARGE_INTEGER

   +0x080 RundownProtect   : _EX_RUNDOWN_REF

   +0x084 UniqueProcessId  : Ptr32 Void

   +0x088 ActiveProcessLinks : _LIST_ENTRY

   +0x090 QuotaUsage       : [3] Uint4B

   +0x09c QuotaPeak        : [3] Uint4B

   +0x0a8 CommitCharge     : Uint4B

   +0x0ac PeakVirtualSize  : Uint4B

   +0x0b0 VirtualSize      : Uint4B

   +0x0b4 SessionProcessLinks : _LIST_ENTRY

   +0x0bc DebugPort        : Ptr32 Void

   +0x0c0 ExceptionPort    : Ptr32 Void

   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE

   +0x0c8 Token            : _EX_FAST_REF

   +0x0cc WorkingSetLock   : _FAST_MUTEX

   +0x0ec WorkingSetPage   : Uint4B

   +0x0f0 AddressCreationLock : _FAST_MUTEX

   +0x110 HyperSpaceLock   : Uint4B

   +0x114 ForkInProgress   : Ptr32 _ETHREAD

   +0x118 HardwareTrigger  : Uint4B

   +0x11c VadRoot          : Ptr32 Void

   +0x120 VadHint          : Ptr32 Void

   +0x124 CloneRoot        : Ptr32 Void

   +0x128 NumberOfPrivatePages : Uint4B

   +0x12c NumberOfLockedPages : Uint4B

   +0x130 Win32Process     : Ptr32 Void

   +0x134 Job              : Ptr32 _EJOB

   +0x138 SectionObject    : Ptr32 Void

   +0x13c SectionBaseAddress : Ptr32 Void

   +0x140 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK

   +0x144 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY

   +0x148 Win32WindowStation : Ptr32 Void

   +0x14c InheritedFromUniqueProcessId : Ptr32 Void

   +0x150 LdtInformation   : Ptr32 Void

   +0x154 VadFreeHint      : Ptr32 Void

   +0x158 VdmObjects       : Ptr32 Void

   +0x15c DeviceMap        : Ptr32 Void

   +0x160 PhysicalVadList  : _LIST_ENTRY

   +0x168 PageDirectoryPte : _HARDWARE_PTE_X86

   +0x168 Filler           : Uint8B

   +0x170 Session          : Ptr32 Void

   +0x174 ImageFileName    : [16] UChar

   +0x184 JobLinks         : _LIST_ENTRY

   +0x18c LockedPagesList  : Ptr32 Void

   +0x190 ThreadListHead   : _LIST_ENTRY

   +0x198 SecurityPort     : Ptr32 Void

   +0x19c PaeTop           : Ptr32 Void

   +0x1a0 ActiveThreads    : Uint4B

   +0x1a4 GrantedAccess    : Uint4B

   +0x1a8 DefaultHardErrorProcessing : Uint4B

   +0x1ac LastThreadExitStatus : Int4B

   +0x1b0 Peb              : Ptr32 _PEB

   +0x1b4 PrefetchTrace    : _EX_FAST_REF

   +0x1b8 ReadOperationCount : _LARGE_INTEGER

   +0x1c0 WriteOperationCount : _LARGE_INTEGER

   +0x1c8 OtherOperationCount : _LARGE_INTEGER

   +0x1d0 ReadTransferCount : _LARGE_INTEGER

   +0x1d8 WriteTransferCount : _LARGE_INTEGER

   +0x1e0 OtherTransferCount : _LARGE_INTEGER

   +0x1e8 CommitChargeLimit : Uint4B

   +0x1ec CommitChargePeak : Uint4B

   +0x1f0 AweInfo          : Ptr32 Void

   +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO

   +0x1f8 Vm               : _MMSUPPORT

   +0x238 LastFaultCount   : Uint4B

   +0x23c ModifiedPageCount : Uint4B

   +0x240 NumberOfVads     : Uint4B

   +0x244 JobStatus        : Uint4B

   +0x248 Flags            : Uint4B

   +0x248 CreateReported   : Pos 0, 1 Bit

   +0x248 NoDebugInherit   : Pos 1, 1 Bit

   +0x248 ProcessExiting   : Pos 2, 1 Bit

   +0x248 ProcessDelete    : Pos 3, 1 Bit

   +0x248 Wow64SplitPages  : Pos 4, 1 Bit

   +0x248 VmDeleted        : Pos 5, 1 Bit

   +0x248 OutswapEnabled   : Pos 6, 1 Bit

   +0x248 Outswapped       : Pos 7, 1 Bit

   +0x248 ForkFailed       : Pos 8, 1 Bit

   +0x248 HasPhysicalVad   : Pos 9, 1 Bit

   +0x248 AddressSpaceInitialized : Pos 10, 2 Bits

   +0x248 SetTimerResolution : Pos 12, 1 Bit

   +0x248 BreakOnTermination : Pos 13, 1 Bit

   +0x248 SessionCreationUnderway : Pos 14, 1 Bit

   +0x248 WriteWatch       : Pos 15, 1 Bit

   +0x248 ProcessInSession : Pos 16, 1 Bit

   +0x248 OverrideAddressSpace : Pos 17, 1 Bit

   +0x248 HasAddressSpace  : Pos 18, 1 Bit

   +0x248 LaunchPrefetched : Pos 19, 1 Bit

   +0x248 InjectInpageErrors : Pos 20, 1 Bit

   +0x248 VmTopDown        : Pos 21, 1 Bit

   +0x248 Unused3          : Pos 22, 1 Bit

   +0x248 Unused4          : Pos 23, 1 Bit

   +0x248 VdmAllowed       : Pos 24, 1 Bit

   +0x248 Unused           : Pos 25, 5 Bits

   +0x248 Unused1          : Pos 30, 1 Bit

   +0x248 Unused2          : Pos 31, 1 Bit

   +0x24c ExitStatus       : Int4B

   +0x250 NextPageColor    : Uint2B

   +0x252 SubSystemMinorVersion : UChar

    +0x253 SubSystemMajorVersion : UChar

   +0x252 SubSystemVersion : Uint2B

   +0x254 PriorityClass    : UChar

   +0x255 WorkingSetAcquiredUnsafe : UChar

   +0x258 Cookie           : Uint4B

 

     ~ 变态的长 ~ 我们可以看到红色标注的那个结构成员 , 这个就是进程句柄表的地址了 , 它是一个 handle_table 结构的东东 ~HOHO~ 看看这个是什么结构  

kd> dt _handle_table

ntdll!_HANDLE_TABLE

   +0x000 TableCode        : Uint4B

   +0x004 QuotaProcess     : Ptr32 _EPROCESS

   +0x008 UniqueProcessId  : Ptr32 Void

   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK

   +0x01c HandleTableList  : _LIST_ENTRY

   +0x024 HandleContentionEvent : _EX_PUSH_LOCK

   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO

   +0x02c ExtraInfoPages   : Int4B

   +0x030 FirstFree        : Uint4B

   +0x034 LastFree         : Uint4B

   +0x038 NextHandleNeedingPool : Uint4B

   +0x03c HandleCount      : Int4B

   +0x040 Flags            : Uint4B

   +0x040 StrictFIFO       : Pos 0, 1 Bit

呵呵 结构大小尚可接受 ~ 那么在这个句柄表内 ~ 我们的句柄信息到底藏在哪里咧 ? 就是在结构变量 TableCode 这里找 , 怎么找咧 ? 看了下网上的 WRK 代码 大概明白他们的思路 原来 TableCode 是个 4 字节的数值 , 这个数值的低 2 位记录着句柄表的级数 , 什么意思咧 ? 一会下面会讲到 , 通过句柄值我们就可以在句柄表内查到所谓的内核对象了 不过这里面又有好多细节的东西 ~ 句柄需要转换才能查到内核对象地址 , 那么我们就边说边做的 涉及到的东西我在补充

 

我们先用 windbg 来看看进程信息 ( 以下涉及到的 WINDBG 命令请到学习笔记 文章去看 ~)

kd> !handle 0 2 6e8

processor number 0, process 000006e8

Searching for Process with Cid == 6e8

PROCESS 812e9408  SessionId: 0  Cid: 06e8    Peb: 7ffd5000  ParentCid: 05e0

    DirBase: 039c0200  ObjectTable: e190e928  HandleCount:  69.

Image: ctfmon.exe

 

Handle table at e18c3000 with 69 Entries in use

 

………………….

 

0114 : Object: e1688498  GrantedAccess: 00000002 Entry: e18c3228

Object: e1688498  Type: (81592560) Section

ObjectHeader: e1688480 (old version)

 

………………….

 

嘿嘿 6e8 是这个进程的 PID   内核对象的句柄值是 0114  其他参数不说明了 自己看帮助吧

我们可以看到 PID 6e8 的进程的一些详细信息 还有这个进程下的 n 多内核对象 我就显示了 1 , 省的看的眼花

那么我们就开始用 windbg 研究这个句柄表吧   首先 我们从上面信息知道 ctfmon.exe 这个进程的内核对象地址是在 812e9408

kd> dt _eprocess 812e9408

ntdll!_EPROCESS

……

   +0x0c4 ObjectTable      : 0xe190e928 _HANDLE_TABLE

……

   +0x174 ImageFileName    : [16]  "ctfmon.exe"

……

 

     省略了 N 多内容 只把重要内容显示出来了 可以看到 这个进程名的确是 ctfmon.exe 那么它所对应的句柄表的位置在这里 :0xe190e928   走到这里瞧下

kd> dt _handle_table 0xe190e928

ntdll!_HANDLE_TABLE

   +0x000 TableCode        : 0xe18c3000

   +0x004 QuotaProcess     : 0x812e9408 _EPROCESS

   +0x008 UniqueProcessId  : 0x000006e8

   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK

   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe189aa0c - 0xe1753414 ]

   +0x024 HandleContentionEvent : _EX_PUSH_LOCK

   +0x028 DebugInfo        : (null)

   +0x02c ExtraInfoPages   : 0

   +0x030 FirstFree        : 0x118

   +0x034 LastFree         : 0

   +0x038 NextHandleNeedingPool : 0x800

   +0x03c HandleCount      : 69

   +0x040 Flags            : 0

   +0x040 StrictFIFO       : 0y0

可以看到 TableCode 的地址在 0xe18c3000 .

这里就得说明下句柄表 , 句柄表分三层 顶层句柄表大小 1K 可存放 256 个元素 , 每个元素占 4bit , 中层表大小 1K 可存放 256 个元素 , 每个元素占 4bit , 下层表大小 2K 可放 256 个元素 每个元素 8bit . 我们可以看到 TableCode 的低 2 位是 0 表示这个句柄表只有 1 , 那么只要句柄值的低 10 位乘以 2 就是该内核对象的指针在句柄表里面的位置了 .

kd> dd e18c3000+(114*2)

e18c3228  e1688481 00000002 00000000 0000011c

e18c3238  00000000 00000120 00000000 00000124

 

看到 e1688481 了吧 ? 这个就是内核对象头的地址 , 由于对象头和对象体偏移量是 0x18, 所以加上 0x18 就可以找到对应的内核对象了

kd> !object e1688481+17

Object: e1688498  Type: (81592560) Section

    ObjectHeader: e1688480 (old version)

    HandleCount: 9  PointerCount: 10

Directory Object: e1432248  Name: ShimSharedMemory

…. 这个是系统是 sp3 , 好像跟 sp2 的有点出入 . 偏移量加 0x17 才是真正的对象头 , 而不是加 0x18….. 麻烦哪位高手解释一下

对比刚才上面那个对象信息

0114 : Object: e1688498  GrantedAccess: 00000002 Entry: e18c3228

Object: e1688498  Type: (81592560) Section

ObjectHeader: e1688480 (old version)

一样的   呵呵 ~~ 刚才大概把用内核句柄查找内核对象的过程演示了一下 , 反正大概操作系统也是这么利用句柄找内核对象的 , 但是 ….. 巨多的疑问随之产生 ….

1.    对象头跟对象体到底是个什么结构什么东西? ?

2.    咋个 sp3 , 对象头和对象体的偏移确实是 0x17 但是我根据句柄找到的对象头的地址跟实际内核的对象头的地址竟然相差 1? 到底是我哪里做错了 ?(解决 ^_^)

3.    其实刚才只是演示了句柄表级数为 0 时的情况 , 还有句柄表为 1 (TableCode 2 位为 01),2 级的 (TableCode 3 位为 10) 的情况 , 但是这个我倒是疑惑不少 , 到底系统是怎么管理这张系统表的 ?

个人理解是这样的 , 如果刚开始分配的的是 0 级表 , 那么可以存放 512 个对象指针 , 但是如果内核对象多出了 512 系统会分配个中层表 , 但是我看到的中层表计算偏移的公式是这样的 TableCode 地址 + 中层偏移 *4+ 低层偏移 *2   那么当内核对象多出 512 的时候内存有是如何分配的呢 ? 貌似跟 2k 的分配方式不同了 ~

 

望高人指点 ~


参考文献:

句柄啊,3层表啊,ExpLookupHandleTableEntry啊...

JIURL玩玩Win2k进程线程篇 HANDLE_TABLE

Windows句柄表格式(2) - XP句柄表格式

补充:  09-1-26 18:30

对于第二个问题  大概有个了解了  哈哈~~  在此多谢实验室大牛指点~!

原来是这样的  在句柄这个32位数中 10~2位这九位是0级表的索引,11~20位是1级表的索引,21~30是2级表的索引,

第三十一位是无效的 为0 但是第0位和第1位不暂时不知道干嘛的 暂且放着  计算索引的时候先将句柄除以四(也就是左移2位的作用)然后在取对以级层的索引


知道了这个关系就好了 我们重新看看我们的句柄是0x114 而且是在0级表做索引

所以先左移两位在乘以8(因为0级表中每个元素占8字节) 0x114/4*8 = 0x114*2

那么我们的句柄在0级表中的地址就应该是

e18c3000+(114*2)


所以有

       kd> dd e18c3000+(114*2)

       e18c3228  e1688481 00000002 00000000 0000011c

    e18c3238  00000000 00000120 00000000 00000124


我们可以看到对应的_HANDLE_TABLE_ENTRY地址应该是

e1688481 但是还要做一些处理才行  由于为了内存对齐(到现在我也不知道这个跟内存对齐有什么关系~) 所以内核对象真实地址的低2位得为0,最高位得为1 那么我们最后一步要做的事情就是把地址规格化

地址 | 0x80000000 & 0xFFFFFFF8  (将最高位置1 低2位置0)

所以真实的地址应该是

e1688481  | 0x80000000 & 0xFFFFFFF8 = e1688480

这个就是对象头的地址 由于对象头的地址大小为0x18  所以加上偏移量18就是对象体的地址

所以对象体的地址就是

e1688480 + 0x18 = e1688498


可以看到 我最后拿!object指令看的那个对象体地址就是e1688498 跟原句柄对应的对象体是一样的~~哈哈  

附上_HANDLE_TABLE_ENTRY结构
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : Ptr32 Void
   +0x000 ObAttributes     : Uint4B
   +0x000 InfoTable        : Ptr32 _HANDLE_TABLE_ENTRY_INFO
   +0x000 Value            : Uint4B
   +0x004 GrantedAccess    : Uint4B
   +0x004 GrantedAccessIndex : Uint2B
   +0x006 CreatorBackTraceIndex : Uint2B
   +0x004 NextFreeTableEntry : Int4B


还有个EXHANDLE结构


typedef struct _EXHANDLE
{
    union
    {
        struct
        {
            ULONG TagBits:2;
            ULONG Index:30;
        };
        HANDLE GenericHandleOverlay;
        ULONG_PTR Value;(2k下没这个成员~)
    };
} EXHANDLE, *PEXHANDLE;



                                                                                                                                                                                                __ay.字



posted on 2009-01-25 17:43 __ay 阅读(2959) 评论(1)  编辑 收藏 引用 所属分类: 操作系统&&内核

Feedback

# re: windbg学习笔记 FOR 内核调试(三) --进程句柄表HANDLE_TABLE 2009-02-02 11:18

不错,楼主继续贴心得啊,一起探讨学习  回复  更多评论   



只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理