想当年
初学核编
,
阅读第三章的内核对象的时候跟看天书没什么感觉
死命在想到底内核对象
,
句柄是个什么东西
干嘛用的
于是我们工作室的老大就对我说
这篇看过就过了
学到后面你自然会明白的
我想也是
,
很多时候感觉学东西的确是这样
暂时看不懂的先放着
过段时间再看回来就恍然大悟了
.
我前段时间又看了下核编的第三章
唯一的收获就是能够大概了解到
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.字