邪恶八进制信息安全团队技术讨论组's Archiver

sunwear 2006-1-5 06:41

[转载]解析Windows NT/2000窗口对象的组织

信息来源:webcrazy([url]http://www.geocities.jp/webcrazyjp/[/url])
文章作者:webcrazy

解析Windows NT/2000窗口对象的组织  
               WebCrazy([url]http://webcrazy.yeah.net/[/url])

   Microsoft Visual Studio提供的Microsoft Spy对Windows的窗口组织有非常直观的表现,在Windows视图下的Window Properties可以显示窗口的很多内容或属性。本文将从Windows NT/2000内核出发,说明这些重要的结构在Windows NT/2000中的最底层的组织。
  先从窗口组织说起,大家知道枚举窗口可以通过EnumWindows、EnumDesktopWindows或EnumChildWindows等这些API来实现。我想真正理解Windows NT/2000实现这些API的细节后,应该可以发现很多Windows NT/2000的内部执行情况,于是我认真的研究了这些API,下面我以Windows 2000 Server Build 2195下EnumWindows API的分析步骤叙述如下:

   1.user32.dll下的EnumWindows首先调用user32.dll下的InternalEnumWindows,然后InternalEnumWindows又调用BuildHwndList(user32.dll).
   2.BuildHwndList调用NtUserBuildHwndList,而这个函数也位于user32.dll模块中。
   3.NtUserBuildHwndList只是调用int 2e指令,然后使用ID为0x112e的System Service,即win32k.sys模块中的NtUserBuildHwndList,请参阅《Windows 2000 System Services列表 》。从这开始也就从用户态进入了核心态。
   4.win32k.sys中的NtUserBuildHwndList继续调用win32k.sys中的BuildHwndList,应该注意的是这两个例程与上面User32.dll中的相应例程同名,这也是我一直强调模块名的原因。BuildHwndList最后调用InternalBuildHwndList(win32k.sys),这个例程才是我们真正感兴趣的地方。InternalBuildHwndList在特定参数下(如EnumDestopWindows API调用,EnumDestopWindows最终也会调用InternalBuildHwndList)是一个递归例程以实现窗口的枚举。

   这个分析只是说明了EnumWindows等API的执行流程,真正的细节还是要看一看代码了。Windows NT/2000下窗口是与特定线程相关联的,即每个线程都可以拥有窗口,最典型的例子是Windows  Explorer(Windows资源管理器),在每打开一个Explorer窗口,explorer进程都会建立线程与这些窗口关联。由于这个原因,Microsoft在内核态的ETHREAD(KTEB)中存储窗口结构(Windows NT早期版本Win32子系统位于用户态,我这未加以说明),这些结构由ETHREAD中的Win32Thread成员指定。Win32Thread在ETHREAD的位置可由Windbg的以下命令找出:

   > !kdex2x86.ethread
   Structure ETHREAD (Size:0x240) member offsets:
   +0000   Tcb(KTHREAD struct)
   +0000    Header(DISPATCHER_HEADER struct)
         .
         .
         .
   +0124    Win32Thread
         .
         .
         .

   要指出一点的是,Win32Thread成员在SoftICE 4.05 for Windows NT中显示为Service Table。为便于用户态代码更容易获得Win32Thread的值,Windows NT/2000也在线程的TEB中存取了Win32Thread指针,在Windows 2000 Server Build 2195其位置位于TEB的后0x40处。user32.dll由下列函数获得Win32Thread的值(i386kd输出):

   kd> x user32!PtiCurrent
   77df3686 user32!PtiCurrent

   kd> u user32!PtiCurrent
   USER32!PtiCurrent:
   77df3686 64a118000000  mov eax,fs:[00000018]
   77df368c 83784000    cmp dword ptr [eax+0x40],0x0
   77df3690 0f8492700200  je USER32!PtiCurrent+0xc (77e1a728)
   77df3696 64a118000000  mov eax,fs:[00000018]
   77df369c 8b4040      mov eax,[eax+0x40]
   77df369f c3        ret

   下面是我实现枚举特定线程拥有的窗口的代码实现(我从KTEB中获得Win32Thread):


   //------------------------------------------------------------
   //
   // BuildHwndList--Enum Thread Windows(SoftICE hwnd Command)
   // Only test on Windows 2000 Server Build 2195 Chinese Edition
   // Build 2195(Free)!Programmed By WebCrazy
   // ([email]tsu00@263.net[/email]  ) on 11-25-2000!
   // Welcome to [url]http://webcrazy.yeah.net[/url]  to get more information
   //
   //------------------------------------------------------------

   #define WIN32THREAD_OFFSET   0x124
   #define HWNDLIST_OFFSET     0xb8
   #define HWNDHANDLE_OFFSET    0x0
   #define HWNDNEXT_OFFSET     0x2c
   #define HWNDPARENT_OFFSET    0x30
   #define HWNDRECT_OFFSET     0x3c
   #define HWNDPROC_OFFSET     0x5c

   //RECT:copied from windef.h
   typedef struct tagRECT
   {
      LONG   left;
      LONG   top;
      LONG   right;
      LONG   bottom;
   } RECT, *PRECT;

   typedef struct tagHWNDRECT{
     RECT WindowRect;
     RECT ClientRect;
   }HWNDRECT,*PHWNDRECT;

   void BuildHwndList(void *kteb)
   {
     PVOID Win32Thread;
     PVOID HwndList;
     PHWNDRECT pHwndRect;

     if(((USHORT)NtBuildNumber)!=2195){
        DbgPrint("Only test on Windows 2000 Server Build 2195!\n");
        return;
     }
     
     Win32Thread=(PVOID)(*(PULONG)((char *)kteb+WIN32THREAD_OFFSET));
     if(!Win32Thread){
       DbgPrint("kteb:%08X isn't a win32 thread!\n",kteb);
       return;
     }

     HwndList=(PVOID)(*(PULONG)((char *)Win32Thread+HWNDLIST_OFFSET));
     if(!HwndList){
       DbgPrint("kteb:%08X isn't a hwnd list!\n",kteb);
       return;
     }
  
     DbgPrint("@kteb %08X first HwndList at %08X\n",kteb,HwndList);
     DbgPrint("HwndList  HWND    PARENT   Window Proc  Window(Client) Rect\n");
     DbgPrint("--------  --------  --------  -----------  -------------------\n");

     do{
       pHwndRect=(PHWNDRECT)((char *)HwndList+HWNDRECT_OFFSET);
       DbgPrint("%08X  %08X  %08X  %08X    %d,%d,%d,%d(%d,%d,%d,%d)\n",
             HwndList,
             *(PULONG)((char *)HwndList+HWNDHANDLE_OFFSET),
             *(PULONG)(*(PULONG)((char *)HwndList+HWNDPARENT_OFFSET)),
             *(PULONG)((char *)HwndList+HWNDPROC_OFFSET),
             pHwndRect->WindowRect.left,pHwndRect->WindowRect.top,
             pHwndRect->WindowRect.right,pHwndRect->WindowRect.bottom,
             pHwndRect->ClientRect.left,pHwndRect->ClientRect.top,
             pHwndRect->ClientRect.right,pHwndRect->ClientRect.bottom);

       HwndList=(PVOID)(*(PULONG)((char *)HwndList+HWNDNEXT_OFFSET));
    }while(HwndList);
   }

   运行一个实例,输出内容大概如下:

   @kteb FF7BB020 first HwndList at A0312DA8
   HwndList  HWND    PARENT   Window Proc  Window(Client) Rect
   --------  --------  --------  -----------  -------------------
   A0312DA8  0001002A  0001000C  77DFF0DF    0,0,0,0(0,0,0,0)
   A0310D50  00010022  0001000C  775331C4    0,0,112,27(4,23,108,23)
   A03176B8  0002004A  0001000C  77DFF0DF    0,0,0,0(0,0,0,0)
   A031A500  00010082  0001000C  76621AC6    44,44,812,581(48,67,808,577)
   A0318FA8  00010062  0001000C  775676F4    0,0,1024,768(0,0,1024,768)

   下面我再讲讲Window Class吧。谈到Class,真的可以想到很多东西,如C++的类等等。至于Window Class我觉的还是引用Microsoft文档的解释吧:

   A window class is a set of attributes that the system uses as a template to create a window. Every
window is a member of a window class. All window classes are process specific.

   从上的说明也可以看出Window Class是与特定进程相关联的。窗口与线程关联,所以其结构存于ETHREAD(KTEB)中,相应的Windows Class的结构则存于EPROCESS(KPEB)中。窗口结构由Win32Thread指定,Window Class则由Win32Process中指定。Win32Process在EPROCESS中的位置也可由windbg看出:

   > !kdextx86.processfields
    EPROCESS structure offsets:
         .
         .
         .
      Win32Process:               0x214
         .
         .
         .

   下面是实现枚举特定进程的Window Class的代码:

   //----------------------------------------------------------------------
   //
   // BuildWindowClassList--Enum Process Window Class(SoftICE class Command)
   // Only test on Windows 2000 Server Build 2195 Chinese Edition
   // Build 2195(Free)!Programmed By WebCrazy([email]tsu00@263.net[/email] ) on 11-25-2000!
   // Welcome to [url]http://webcrazy.yeah.net[/url] to get more information
   //
   //----------------------------------------------------------------------

   #define WIN32PROCESS_OFFSET     0x214
   #define WINPRIVATECLASS_OFFSET   0x38
   #define WINGLOBALCLASS_OFFSET    0x3c

   #define CLASS_CLASSSTYLE_OFFSET  0x2c
   #define CLASS_WINDOWPROC_OFFSET  0x30
   #define CLASS_MODULEBASE_OFFSET  0x3c
   #define CLASS_CLASSNAME_OFFSET   0x50

   void BuildWindowClassList(void *kpeb)
   {
      PSINGLE_LIST_ENTRY pPrivateClassList,pGlobalClassList;
      PVOID Win32Process;
      PCHAR pClassName;

      Win32Process=(PVOID)(*(PULONG)((char *)kpeb+WIN32PROCESS_OFFSET));
      if(!Win32Process){
        DbgPrint("kpeb:%08X isn't a win32 process!\n",kpeb);
        return;
      }

      pPrivateClassList=(PSINGLE_LIST_ENTRY)(*(PULONG)((char *)Win32Process+WINPRIVATECLASS_OFFSET));
      if(pPrivateClassList){
        DbgPrint("Application(kpeb:%08X) Private Class List:\n",kpeb);
        DbgPrint("%-35sHandle   ModBase  WinProc  Styles\n","Class Name");
        DbgPrint("%-35s--------  --------  --------  --------\n","----------");

        do{

          pClassName=(PCHAR)(*(PULONG)((char *)pPrivateClassList+CLASS_CLASSNAME_OFFSET));
          DbgPrint("%-35s%08X  %08X  %08X  %08X\n",pClassName,pPrivateClassList,
                *(PULONG)((char *)pPrivateClassList+CLASS_MODULEBASE_OFFSET),
                *(PULONG)((char *)pPrivateClassList+CLASS_WINDOWPROC_OFFSET),
                *(PULONG)((char *)pPrivateClassList+CLASS_CLASSSTYLE_OFFSET));

          pPrivateClassList=pPrivateClassList->Next;
        }while(pPrivateClassList);
      }

      pGlobalClassList=(PSINGLE_LIST_ENTRY)(*(PULONG)((char *)Win32Process+WINGLOBALCLASS_OFFSET));
      if(pGlobalClassList){
        DbgPrint("Application(kpeb:%08X) Global Class List:\n",kpeb);
        DbgPrint("%-35sHandle   ModBase  WinProc  Styles\n","Class Name");
        DbgPrint("%-35s--------  --------  --------  --------\n","----------");

        do{

          pClassName=(PCHAR)(*(PULONG)((char *)pGlobalClassList+CLASS_CLASSNAME_OFFSET));
          DbgPrint("%-35s%08X  %08X  %08X  %08X\n",pClassName,pGlobalClassList,
                *(PULONG)((char *)pGlobalClassList+CLASS_MODULEBASE_OFFSET),
                *(PULONG)((char *)pGlobalClassList+CLASS_WINDOWPROC_OFFSET),
                *(PULONG)((char *)pGlobalClassList+CLASS_CLASSSTYLE_OFFSET));

          pGlobalClassList=pGlobalClassList->Next;
        }while(pGlobalClassList);
      }
   }

   照例是一个运行实例的结果:

   Application(kpeb:FF5BA3C0) Private Class List:
   Class Name                 Handle   ModBase  WinProc  Styles
   ----------                 --------  --------  --------  --------
   ConsoleIMEClass              A031F938  01000000  0100152E  00000000
   DDEMLUnicodeServer            A031F8A8  77DF0000  77E2E2F9  00000000
   DDEMLAnsiServer              A031F820  77DF0000  77E2E2F9  00000000
   DDEMLUnicodeClient            A031F790  77DF0000  77E2E14D  00000000
   DDEMLAnsiClient              A031F708  77DF0000  77E2E14D  00000000
   DDEMLMom                  A031F688  77DF0000  77DFD316  00000000
   Application(kpeb:FF5BA3C0) Global Class List:
   Class Name                 Handle   ModBase  WinProc  Styles
   ----------                 --------  --------  --------  --------
   Static                    A031F610  77DF0000  77E000F9  00004088
   IME                      A031F5A0  77DF0000  77DFF0DF  00004000
         .
         .
         .

   上面我给出的两个代码段,分别实现了SoftICE中的hwnd与class命令的功能。在分析了这两个命令后,也就可以进一步分析Windows NT/2000内部消息机制,这是Windows GUI实现的基础。可以继续挖掘的东西看来还越来越多了。

参考资料:
    1.David Solomom《Inside Windows NT,2nd Edition》

页: [1]
© 1999-2008 EvilOctal Security Team