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)
一樣的 呵呵 ~~ 剛才大概把用核心句柄查找核心對象的過程示範了一下 , 反正大概作業系統也是這麼利用句柄找核心對象的 , 但是 ….. 巨多的疑問随之産生 ….
<!--[if !supportLists]--> 1. <!--[endif]--> 對象頭跟對象體到底是個什麼結構什麼東西? ?
<!--[if !supportLists]--> 2. <!--[endif]--> 咋個 sp3 下 , 對象頭和對象體的偏移确實是 0x17 但是我根據句柄找到的對象頭的位址跟實際核心的對象頭的位址竟然相差 1? 到底是我哪裡做錯了 ?(解決 ^_^)
<!--[if !supportLists]--> 3. <!--[endif]--> 其實剛才隻是示範了句柄表級數為 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;