图5-1. KeServiceDescriptorTable的结构图
typedef NTSTATUS (NTAPI*NTPROC)();
typedef NTPROC* PNTPROC;
#define NTPROC_ sizeof(NTPROC)
typedef struct _SYSTEM_SERVICE_TABLE
{
PNTPROC ;ServiceTable;// array of entry points
PDOWRD; CounterTable;// array of usage counters
DWordServiceLimit;;// number of table entries
PBYTE; ;;ArgumentTable; ;// array of byte counts
}
SYSTEM_SERVICE_TABLE,
*PSYSTEM_SERVICE_TABLE,
**PPSYSTEM_SERVICE_TABLE;
//-----------------------------------------------------------------------------------------------------------
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnl// ntoskrnl.exe ( native api )
SYSTEM_SERVICE_TABLE win32k;;// win32k.sys (gdi/user support)
SYSTEM_SERVICE_TABLE Table3;;// not used
SYSTEM_SERVICE_TABLE Table4;;// not used
}
SYSTEM_DESCRIPTOR_TABLE,
*PSYSTEM_DESCRIPTOR_TABLE,
**PPSYSTEM_DESCRIPTOR_TABLE;
列表5-1. SERVICE_DESCRIPTOR_TABLE结构的定义
Windows 2000还提供了另一个服务描述符表参数块----KeServiceDescriptorTableShadow 。不过,KeServiceDescriptorTable已经由ntoskrnl.exe公开的导出了,因此,内核模式的驱动程序可以很容易的访问它,而KeServiceDescriptorTableShadow则不行 。在Windows 2000下,KeServiceDescriptorTableShadow紧随KeServiceDescriptorTable之后,但是你不能在Windows NT中以这样的方法找到它,因为,这一规则并不使用于Windows NT 。可能在Windows 2000的后续版本的中,这种方法也不行 。这两个参数块的不同之处在于:KeServiceDescriptorTableShadow中的第二个项被系统使用了,用来指向内部的W32pServiceTable和w32pArgumentTable结构,Win32的内核模式组件Win32K.sys使用这两个结构来分派自己的API调用,如图5-2所示 。KiSystemService()通过检查EAX中索引值的第12和13位来确认是不是应该由Win32K.sys处理API调用 。如果这两个位都是0,则是由ntoskrnl.exe处理的Native API调用,因此KiSystemService()使用第一个SDT 。如果第12位为1并且第13位为0,KiSystemService()使用第二个SDT,这个SDT并没有被当前系统使用 。这意味着Native API调用的索引值的潜在范围是:0x0000 --- 0x0FFFF,Win32K.sys调用使用的索引范围是:0x1000 --- 0x1FFF 。因此,0x2000 --- 0x2FFF和0x3000 --- 0x3FFF保留给剩下的两个SDT 。在Windows 2000中,Native API服务表包含248个项,Win32K.sys表包含639个项 。
图5-2. KeServiceDescriptorTableShadow的结构图
Russinovich和Cogswell的独具特色的方法是:通过简单的向KiServiceTable数组中放入一个不同的处理例程来hook所有API调用 。这个处理例程最终会调用位于ntoskrnl.exe中的原始处理例程,但它有机会察看一下被调用函数的输入/输出参数 。这个方法非常强大却又如此简单 。因为所有用户模式的线程必须经过这个“针眼”才能获得Native API的服务,安装一个全局hook来简单的替换一个函数指针的方法,在启动一个新的进程和线程的情况下,也能很稳定的工作 。这并不需要一种通讯机制来通知新加入或将要移除的进程/线程 。
不幸的是,系统服务表在不同Windows NT版本上不相同 。表5-1比较了Windows NT/2000的KiServiceTable 。很显然,不仅是处理例程的号码从211增加到了248,而且新的处理例程并不是直接添加到列表的末尾,而是被插入到了各个地方!因此,一个服务函数索引,如0x20在Windows 2000中指向NtCreateFile(),而在Windows NT中却指向NtCreateProfile() 。所以,通过操作服务函数表进行hook的API调用监控器必须小心的检查它所在的Windows NT的版本 。这可以通过如下几个方式来完成:
l 一种可能性是,检查由ntoskrnl.exe导出的公开变量:NtBuildNumber,就像Russinovich和Cogswell在他们的原文中所作的那样 。Windows NT 4.0为所有Service Pack提供的Build Number是:1381 。Windows 2000的当前Build Number是:2195 。看来有希望,这个版本号在Windows NT的早期版本中很稳定 。
推荐阅读
- 古诗小松拼音版 古诗小松杜荀鹤拼音
- 1 《Undocumented Windows 2000 Secrets》翻译 --- 2
- 圣斗士里的教皇
- 交房需要带什么手续
- 在Windows NT域和Windows 2000域之间建立信任关系
- 移花接木 解决Windows 2000无法启动的问题
- 使用命令行参数为系统打补丁
- 生子文勋鹿古风重生
- 克隆windows 2000管理员帐号
- Windows 2000 优化完全攻略
