Flier's Sky

天空,蓝色的天空,眼睛看不到的东西,眼睛看得到的东西

导航

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

Posted on 2004-07-08 10:43  Flier Lu  阅读(1254)  评论(0编辑  收藏  举报
另一种获取系统服务描述表入口地址的方法

http://www.blogcn.com/user8/flier_lu/index.html?id=1442740&run=.0F766FA

    在《自动获取 NT 系统服务描述表与函数名映射表》一文中我使用MS提供的DbgHelp库,从符号库文件中查找KeServiceDescriptorTable和KeServiceDescriptorTableShadow符号,以获取系统服务描述表入口地址。这种方法逻辑简单,但是对不同操作系统版本的调试符号文件有依赖性,不适用于作为工具被散发出去的程序。因此这儿给出另外一种从线程本身的特性着手获取系统服务描述表入口地址的方法。
     我们所说的线程,实际上分为核心态和用户态两部分。Win32下这两者基本上是1对1的关系,其他平台如Solaris或Linux 2.6以前的版本则使用不同的映射模型。而Win32系统中核心态的线程,实际上也分为两类:工作线程和GUI线程。前者是建立核心线程的缺省类型,后者在线程第一次使用Win32k.sys系统服务时自动转换,或者使用PsConvertToGuiThread函数(ntospspsquery.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的ServiceTable字段,即可 :D 这里的0x124偏移是Win2003下的KTHREAD结构,在Win2k下此偏移为0xDC,其他版本还需要做相应调整。
 
以下为引用:

 0:001> dt nt!_KTHREAD
    +0x000 Header           : _DISPATCHER_HEADER
    ...
    +0x120 Affinity         : Uint4B
    +0x124 ServiceTable     : Ptr32 Void
    +0x128 ApcStatePointer  : [2] Ptr32 _KAPC_STATE
    ...