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里面