另一种获取系统服务描述表入口地址的方法

 

在《自动获取 NT 系统服务描述表与函数名映射表》一文中我使用MS提供的DbgHelp库,从符号库文件中查找KeServiceDescriptorTable和KeServiceDescriptorTableShadow符号,以获取系统服务描述表入口地址。这种方法逻辑简单,但是对不同操作系统版本的调试符号文件有依赖性,不适用于作为工具被散发出去的程序。因此这儿给出另外一种从线程本身的特性着手获取系统服务描述表入口地址的方法。
我们所说的线程,实际上分为核心态和用户态两部分。Win32下这两者基本上是1对1的关系,其他平台如Solaris或Linux 2.6以前的版本则使用不同的映射模型。而Win32系统中核心态的线程,实际上也分为两类:工作线程和GUI线程。前者是建立核心线程的缺省类型,后者在线程第一次使用Win32k.sys系统服务时自动转换,或者使用PsConvertToGuiThread函数(ntos/ps/psquery.c:3247)显式转换。两者之间的区别主要在于使用的资源缺省大小不同,以及使用的系统服务描述表不同。这也是为什么系统服务描述表要分为KeServiceDescriptorTable和KeServiceDescriptorTableShadow的原因之一,后者包括前者没有的对GDI服务的入口函数地址,一般在Win32k.sys中实现。核心线程对象的ETHREAD::KTHREAD::ServiceTable字段保存了此线程适用的系统调用服务表地址,此字段也被PsConvertToGuiThread函数用于判断线程类型。功能与Windows XP/2003提供的IsGUIThread函数类型。
使用上我们可以创建一个线程,此线程不做任何实际工作,只是根据我们要取哪个系统服务描述表来决定是否调用GDI函数,如

以下为引用:

class TGuiThread : public TThread
{
public:
TGuiThread(void) : TThread(false)
{
FreeOnTerminate = false;
}

void __fastcall Execute(void)
{
::GetDesktopWindow();
}
};

在需要获取地址时,我们可以创建一个此线程的实例,然后通过其句柄获取内核对象地址。

以下为引用:

//---------------------------------------------------------------------------
DWORD TServiceTableApplication::GetpKeServiceDescriptorTableAddress(void) const

{
std::auto_ptr<TGuiThread> GuiThread(new TGuiThread());

GuiThread->WaitFor();

::THandleTable tblHandles;

PVOID pObj = NULL;
TSystemHandleList& handles = tblHandles.HandleByProcessID[::GetCurrentProcessId()];

for(TSystemHandleCPtr itHandle = handles.begin();
itHandle != handles.end(); itHandle++)
{
if((HANDLE)itHandle->Handle == (HANDLE)GuiThread->Handle)
{
pObj = itHandle->Object;
break;
}
}

assert(pObj);

LPVOID lpAddr;

TPhysicalMemoryManager::Default().ReadVirtualMemory((LPCVOID)((DWORD)pObj + 0x124), &lpAddr, sizeof(lpAddr));

return lpAddr;
}

然后读取ETHREAD内核对象偏移0x124(win2003为0x124,win2000sp4为0xDC,其他操作系统版本可能有区别)的ServiceTable字段,即可 :D

以下为引用:

0:001> dt nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
...
+0x120 Affinity : Uint4B
+0x124 ServiceTable : Ptr32 Void
+0x128 ApcStatePointer : [2] Ptr32 _KAPC_STATE
...
---
说了世上一无牵挂为何有悲喜
说了朋友相交如水为何重别离
说了少年笑看将来为何常回忆
说了青春一去无悔为何还哭泣
tombkeeper 发表于:2004-03-24 22:26

发帖: 2279
注册: 2001-01-16
/*
作为该flier程序的第一个小白鼠,我在2000上运行的时候就蹦出了非法访问的框子,
bug提交Flier后,发现是ServiceTable的偏移在各系统上不同导致的。
作为小白鼠,我有义务找个通用的办法。

ServiceTable在ETHREAD中的偏移,每个操作系统各不相同,但是在NTOSKRNL.EXE的系
统调用入口部分用到了这个偏移,所以我们可以用搜索代码的方式将这个偏移找出来。
tombkeeper#nsfocus.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

char * get_func(char str[],char moduleName[])
{
char *ret = 0;
char *b = (char*)LoadLibrary(moduleName);
int i;
__try
{
for (i=0; i<0x100000; i++, b++)
{
if (memcmp(b, str, 12) == 0)
{
printf("ServiceTable offset: 0x%.8x/n",*(DWORD *)(b+12));
}
}
}
__except(1){}

return ret;
}

void main()
{
int i = 0;
char * funcAddr = 0;
static char moduleName[]="NTOSKRNL.EXE";
static char str[] = {
0x8B,0xF8,0xC1,0xEF,0x08,0x83,
0xE7,0x30,0x8B,0xCF,0x03,0xBE,
0xDC,0x00,0x00,0x00
};
get_func(str,moduleName);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值