setwindowhook内部原理

setwindowhook  ->  intsetwindowhookex  ->  NtUserSetWindowHookEx(工作在win32k.sys子系统层)

Hook = UserCreateObject(gHandleTable, NULL, &Handle, otHook, sizeof(HOOK));

同属于user32的用户对象的读写接口

UserGetWindowObject   获取window对象 

UserCreateObject  

UserGetObject

以下是handle和 用户对象地址的映射关系

handle是 用户对象在用户全局对象列表中的序号的算法结果(有一个专门的算法可以使用), 有两个函数专门处理 index和对象地址之间的映射

PUSER_HANDLE_ENTRY handle_to_entry(PUSER_HANDLE_TABLE ht, HANDLE handle )
{
   unsigned short generation;
   int index = (((unsigned int)handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
   if (index < 0 || index >= ht->nb_handles)
      return NULL;
   if (!ht->handles[index].type)
      return NULL;
   generation = (unsigned int)handle >> 16;
   if (generation == ht->handles[index].generation || !generation || generation == 0xffff)
      return &ht->handles[index];
   return NULL;
}

用户对象的原型

typedef struct _USER_HANDLE_ENTRY
{
    void          *ptr;          /* pointer to object */
    union
    {
        PVOID pi;
        PTHREADINFO pti;          // pointer to Win32ThreadInfo
        PPROCESSINFO ppi;         // pointer to W32ProcessInfo
    };
    unsigned char  type;         /* object type (0 if free) */
    unsigned char  flags;
    unsigned short generation;   /* generation counter */
} USER_HANDLE_ENTRY, * PUSER_HANDLE_ENTRY;

win32子系统介绍

里面包含了所有用户对象的创建, hook机制的核心实现,
线程hook和系统hook

线程hook不允许

 WH_JOURNALRECORD
 WH_JOURNALPLAYBACK ||
 WH_KEYBOARD_LL ||
 WH_MOUSE_LL ||
 WH_SYSMSGFILTER)

用户空间的用户对象有以下定义

typedef enum _USER_OBJECT_TYPE
{
  otFree = 0,
  otWindow,
  otMenu,
  otCursorIcon,
  otSMWP,
  otHook,      //hook钩子
  otClipBoardData,
  otCallProc,
  otAccel,
  otDDEaccess,
  otDDEconv,
  otDDExact,
  otMonitor,
  otKBDlayout,
  otKBDfile,
  otEvent,
  otTimer,
  otInputContext,
  otHidData,
  otDeviceInfo,
  otTouchInput,
  otGestureInfo
} USER_OBJECT_TYPE;

桌面窗口的内部结构

typedef struct _DESKTOP
{
    PDESKTOPINFO pDeskInfo;
    LIST_ENTRY ListEntry;
    /* Pointer to the associated window station. */
    struct _WINSTATION_OBJECT *rpwinstaParent;
    PWND spwndForeground;
    PWND spwndTray;
    PWND spwndMessage;
    PWND spwndTooltip;
    PSECTION_OBJECT hsectionDesktop;
    PWIN32HEAP pheapDesktop;
    ULONG_PTR ulHeapSize;
    LIST_ENTRY PtiList;
    /* use for tracking mouse moves. */
    PWND spwndTrack;
    DWORD htEx;
    RECT rcMouseHover;
    DWORD dwMouseHoverTime;

    /* ReactOS */
    /* Pointer to the active queue. */
    PVOID ActiveMessageQueue;
    /* Handle of the desktop window. */
    HANDLE DesktopWindow;
    /* Thread blocking input */
    PVOID BlockInputThread;
    LIST_ENTRY ShellHookWindows;
} DESKTOP, *PDESKTOP;

用户对象内部分配 句柄是采用递增的方式, 真实的情况如下:

__inline static PUSER_HANDLE_ENTRY alloc_user_entry(PUSER_HANDLE_TABLE ht)
{
   PUSER_HANDLE_ENTRY entry;

   DPRINT("handles used %i\n",gpsi->cHandleEntries);

   if (ht->freelist)
   {
      entry = ht->freelist;
      ht->freelist = entry->ptr;

      gpsi->cHandleEntries++;
      return entry;
   }

   if (ht->nb_handles >= ht->allocated_handles)  /* need to grow the array */
   {
/**/
      int i, iFree = 0, iWindow = 0, iMenu = 0, iCursorIcon = 0,
          iHook = 0, iCallProc = 0, iAccel = 0, iMonitor = 0, iTimer = 0;
 /**/
      DPRINT1("Out of user handles! Used -> %i, NM_Handle -> %d\n", gpsi->cHandleEntries, ht->nb_handles);
//#if 0
      for(i = 0; i < ht->nb_handles; i++)
      {
         switch (ht->handles[i].type)
         {
           case otFree: // Should be zero.
            iFree++;
            break;
           case otWindow:
            iWindow++;
            break;
           case otMenu:
            iMenu++;
            break;
           case otCursorIcon:
            iCursorIcon++;
            break;
           case otHook:
            iHook++;
            break;
           case otCallProc:
            iCallProc++;
            break;
           case otAccel:
            iAccel++;
            break;
           case otMonitor:
            iMonitor++;
            break;
           case otTimer:
            iTimer++;
            break;
           default:
            break;
         }
      }
      DPRINT1("Handle Count by Type:\n Free = %d Window = %d Menu = %d CursorIcon = %d Hook = %d\n CallProc = %d Accel = %d Monitor = %d Timer = %d\n",
      iFree, iWindow, iMenu, iCursorIcon, iHook, iCallProc, iAccel, iMonitor, iTimer );
//#endif
      return NULL;
#if 0
      PUSER_HANDLE_ENTRY new_handles;
      /* grow array by 50% (but at minimum 32 entries) */
      int growth = max( 32, ht->allocated_handles / 2 );
      int new_size = min( ht->allocated_handles + growth, (LAST_USER_HANDLE-FIRST_USER_HANDLE+1) >> 1 );
      if (new_size <= ht->allocated_handles)
         return NULL;
      if (!(new_handles = UserHeapReAlloc( ht->handles, new_size * sizeof(*ht->handles) )))
         return NULL;
      ht->handles = new_handles;
      ht->allocated_handles = new_size;
#endif
   }

   entry = &ht->handles[ht->nb_handles++];

   entry->generation = 1;

   gpsi->cHandleEntries++;

   return entry;
}

//  windows的默认处理函数  里面的钩子是怎么弄的呢, 请看

callwindowproc

检查这连个全局变量 ghmodUserApiHook   gfUserApiHook 的指, 如果不为空,则有hook

callwindowproc实际上是一个分发器, 分发器分发给谁了呢, 有以下朋友们来一一做掉

USERAPIHOOK guah =
{
  sizeof(USERAPIHOOK),
  (WNDPROC)RealDefWindowProcA,
  (WNDPROC)RealDefWindowProcW,
  {NULL, 0},
  (FARPROC)RealGetScrollInfo,
  (FARPROC)RealSetScrollInfo,
  (FARPROC)NtUserEnableScrollBar,
  (FARPROC)RealAdjustWindowRectEx,
  (FARPROC)NtUserSetWindowRgn,
  (WNDPROC_OWP)DefaultOWP,
  (WNDPROC_OWP)DefaultOWP,
  {NULL, 0},
  (WNDPROC_OWP)DefaultOWP,
  (WNDPROC_OWP)DefaultOWP,
  {NULL, 0},
  (FARPROC)RealGetSystemMetrics,
  (FARPROC)RealSystemParametersInfoA,
  (FARPROC)RealSystemParametersInfoW,
  (FARPROC)ForceResetUserApiHook,
  (FARPROC)RealDrawFrameControl,
  (FARPROC)NtUserDrawCaption,
  (FARPROC)RealMDIRedrawFrame,
  (FARPROC)GetRealWindowOwner,
};

RealDefWindowProcA这个函数里面我们发现了spy++的实现原理, 是通过对消息的过滤处理,记录了下来  实现在user32.dll里面

posted @ 2011-08-04 21:11  麦峰强  阅读(3223)  评论(0编辑  收藏  举报