首页
社区
课程
招聘
[原创]Hook CreateTextServices
发表于: 2013-8-31 11:02 19244

[原创]Hook CreateTextServices

2013-8-31 11:02
19244
大牛飘过,最近想到了CreateTextServices来记录消息
首先想办法注入到QQ进程内,我是劫持HummerEngine.dll

以前大家都是调用ITextServices::TxGetText获取消息记录,而我想到了Hook ITextServices的虚函数表,我Hook它三个成员函数TxSendMessage、TxGetText、TxSetText。

我们以前追加编辑框文本的时候都是先发生EM_SETSEL,再发生EM_REPLACESEL。QQ的无窗口编辑控件也是如此,因此我们只要拦截ITextServices::TxSendMessage,就能记录到聊天记录。

但是Hook了ITextServices::TxSendMessage了,却得不到聊天的窗口句柄,因此我只能以this指针为文件名来追加消息记录。Hook ITextServices::TxSendMessage有个好处,只要在编辑框里面有的内容都能记录下来,就不怕对方清屏了。另外QQ可能有很多无窗口编辑框,但只有聊天消息的编辑框的文本模式为TM_RICHTEXT,发生EM_GETTEXTMODE消息判断下就行了

剩下的是代码,大家凑合着看,反正我在64位QQ2013sp1可以记录到,至于32位由于有保护的原因,不能劫持HummerEngine.dll

#include <Windows.h>
#include <Tchar.h>
#include <Stdio.h>
#include <Richedit.h>
#include <Textserv.h>

#include "mhook-lib\mhook.h"

#pragma comment(linker, "/EXPORT:RunQQHummerEngine=HummerEngineX.RunQQHummerEngine,PRIVATE")
#pragma comment(linker, "/EXPORT:UninitializeCom=HummerEngineX.UninitializeCom,PRIVATE")

typedef DWORD (WINAPIV *PGetSelfUin)(VOID);

HMODULE 		g_hinst;
HMODULE			g_hRiched20;
BOOL			g_bInitial;
TCHAR			g_szRecordPath[MAX_PATH];
DWORD			g_dwUin;

PCreateTextServices		g_pfnCreateTextServices;
PGetSelfUin				g_pfnGetSelfUin;

//追加文本文件
VOID AppendTextFile(LPTSTR pszFilePath, LPTSTR pszText)
{
	HANDLE			hFile;
	DWORD			dwFileSize;
	DWORD			dwRetLen;

	hFile = CreateFile(pszFilePath,
		GENERIC_WRITE,
		0,
		NULL,
		OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		if(GetLastError() == ERROR_ALREADY_EXISTS)
		{
			dwFileSize = GetFileSize(hFile, NULL);
			SetFilePointer(hFile, dwFileSize, NULL, FILE_BEGIN);
		}
		else
		{
#ifdef UNICODE
			UCHAR			szBigUtf16Hd[] = {0xFF, 0xFE};

			WriteFile(hFile, &szBigUtf16Hd, sizeof(szBigUtf16Hd), &dwRetLen, NULL);
#endif
		}

		WriteFile(hFile, pszText, lstrlen(pszText) * sizeof(TCHAR), &dwRetLen, NULL);

		CloseHandle(hFile);
	}
}

class CHookTextServices
{
public:
	HRESULT TxSendMessage(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult)
	{
		ITextServices		*pTextServices = (ITextServices*)this;
		
		switch(msg)
		{
		case EM_REPLACESEL:
			{
				SYSTEMTIME		systemTime;
				LPTSTR			pszStr;
				TCHAR			szFormat[512];
				LRESULT			lRes;
				
				pszStr = (LPTSTR)lparam;
				if(lstrcmpi(pszStr, _T("")) == 0)
					break;

				if(g_bInitial == FALSE)
					break;

				if((pTextServices->TxSendMessage(EM_GETTEXTMODE, 0, 0, &lRes) == S_OK) && (lRes & TM_RICHTEXT))
				{
					GetSystemTime(&systemTime);
					wsprintf(szFormat, _T("%s\\%lu\\%02X%02X%02X%p.txt"), 
						g_szRecordPath,
						g_dwUin,
						systemTime.wYear,
						systemTime.wMonth,
						systemTime.wDay,
						this);

					AppendTextFile(szFormat, (LPWSTR)lparam);
				}
			}
			break;
		}
		return (pTextServices->*(g_pHookTextServices->TrueTxSendMessage))(msg, wparam, lparam, plresult);
	}

	HRESULT TxGetText(BSTR *pbstrText)
	{
		ITextServices		*pTextServices = (ITextServices*)this;
		
		return (pTextServices->*(g_pHookTextServices->TrueTxGetText))(pbstrText);
	}

	HRESULT TxSetText(LPCWSTR pszText)
	{
		ITextServices		*pTextServices = (ITextServices*)this;
		
		return (pTextServices->*(g_pHookTextServices->TrueTxSetText))(pszText);
	}

public:
	HRESULT (ITextServices::* TrueTxSendMessage)(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult);
	HRESULT (ITextServices::* TrueTxGetText)(BSTR *pbstrText);
	HRESULT (ITextServices::* TrueTxSetText)(LPCWSTR pszText);

	HRESULT (CHookTextServices::* HookTxSendMessage)(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult);
	HRESULT (CHookTextServices::* HookTxGetText)(BSTR *pbstrText);
	HRESULT (CHookTextServices::* HookTxSetText)(LPCWSTR pszText);

} *g_pHookTextServices;

VOID VFSet(LPVOID *ppVfPtr, LPVOID pAddr)
{
	DWORD			dwOldProtect;
	
	if(VirtualProtect(ppVfPtr,
		sizeof(LPVOID),
		PAGE_EXECUTE_READWRITE,
		&dwOldProtect))
	{
		*ppVfPtr = pAddr;
		VirtualProtect(ppVfPtr,
			sizeof(LPVOID),
			dwOldProtect,
			&dwOldProtect);
	}
}

HRESULT STDAPICALLTYPE NewCreateTextServices(
	IUnknown *punkOuter,
	ITextHost *pITextHost,
	IUnknown **ppUnk
	)
{
	HRESULT				hr;
	LPIID				pIID_ITS;
	ITextServices		*pTextServices;
	LPVOID				*ppVtPtr;
	LPVOID				*ppVfPtr;

	HMODULE				hKernelUtil;
	TCHAR				szFormat[512];
	DWORD				cbValue;

	if(g_bInitial == FALSE)
	{
		if((hKernelUtil = GetModuleHandle(_T("KernelUtil.dll"))) &&
			(g_pfnGetSelfUin = (PGetSelfUin)GetProcAddress(hKernelUtil, "?GetSelfUin@Contact@Util@@YAKXZ")))
		{
			if(g_dwUin = g_pfnGetSelfUin())
			{
				//读取记录文件夹路径
				cbValue = sizeof(g_szRecordPath);
				if(ReadRegValue(HKEY_LOCAL_MACHINE,
					_T("SOFTWARE\\QQMsg"),
					_T("RecordFilePath"),
					g_szRecordPath,
					&cbValue))
				{
					g_bInitial = TRUE;

					wsprintf(szFormat, _T("%s\\%lu"), g_szRecordPath, g_dwUin);
					CreateDirectory(szFormat, NULL);
				}
			}
		}
	}

	hr = g_pfnCreateTextServices(punkOuter,
		pITextHost,
		ppUnk);
	if(hr == S_OK)
	{
		pIID_ITS = (LPIID)GetProcAddress(g_hRiched20, "IID_ITextServices");
		if((*ppUnk)->QueryInterface(*pIID_ITS, 
			(LPVOID*)&pTextServices) == S_OK)
		{
			if(g_pHookTextServices == NULL)//虚函数Hook应用于所有实例,因此只需Hook一遍就行了
			{
				g_pHookTextServices = new CHookTextServices;

				ppVtPtr = *(LPVOID**)pTextServices;

				ppVfPtr = &ppVtPtr[3];
				*(LPVOID*)&g_pHookTextServices->TrueTxSendMessage = *ppVfPtr;
				g_pHookTextServices->HookTxSendMessage = &CHookTextServices::TxSendMessage;
				VFSet(ppVfPtr, *(LPVOID*)&g_pHookTextServices->HookTxSendMessage);
				
				ppVfPtr = &ppVtPtr[13];
				*(LPVOID*)&g_pHookTextServices->TrueTxGetText = *ppVfPtr;
				g_pHookTextServices->HookTxGetText = &CHookTextServices::TxGetText;
				VFSet(ppVfPtr, *(LPVOID*)&g_pHookTextServices->HookTxGetText);
				
				ppVfPtr = &ppVtPtr[14];
				*(LPVOID*)&g_pHookTextServices->TrueTxSetText = *ppVfPtr;
				g_pHookTextServices->HookTxSetText = &CHookTextServices::TxSetText;
				VFSet(ppVfPtr, *(LPVOID*)&g_pHookTextServices->HookTxSetText);
			}
			
			pTextServices->Release();
		}
	}

	return hr;
}

BOOL WINAPI DllMain(
	HINSTANCE hinstDLL,
	DWORD fdwReason, 
	LPVOID lpvReserved
	)
{
	switch(fdwReason)
	{
	case DLL_PROCESS_ATTACH://加载dll;
		{
			g_hinst = hinstDLL;			
			g_hRiched20 = LoadLibrary(_T("RICHED20.DLL"));

			g_pfnCreateTextServices = (PCreateTextServices)GetProcAddress(g_hRiched20, "CreateTextServices");
			Mhook_SetHook((PVOID*)&g_pfnCreateTextServices, NewCreateTextServices);
		}
		break;
	case DLL_PROCESS_DETACH://释放dll
		{
			Mhook_Unhook((PVOID*)&g_pfnCreateTextServices);
			g_pfnCreateTextServices = NULL;

			FreeLibrary(g_hRiched20);
		}
		break;
	case DLL_THREAD_ATTACH://新建线程
		{
		}
		break;
	case DLL_THREAD_DETACH://线程退出
		{
		}
		break;
	}

	return TRUE;
}


Win7 64位测试有用
QQMsg.zip

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 5
支持
分享
最新回复 (13)
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
make
2013-8-31 12:29
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark下,感谢分享
2013-8-31 16:46
0
雪    币: 778
活跃值: (208)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
4
自己顶一个
2013-9-7 18:27
0
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
我上次在谷歌搜索RICHED20.DLL就找到一篇博文说hook这个函数的,呵呵
2013-9-7 19:22
0
雪    币: 778
活跃值: (208)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
6
我这个可是采用虚函数钩子,绝对是首家,就这个虚函数钩子花了我几天时间
2013-9-7 19:44
0
雪    币: 778
活跃值: (208)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
7
以前的虚函数钩子成员函数都是标准调用,但是ITextServices成员函数都不是标准调用约定,因此以往的虚函数钩子都不可用
2013-9-7 19:46
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
8
mark
2013-9-7 20:29
0
雪    币: 372
活跃值: (36)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
我只能说楼主 你他,,妈很强大
2013-9-8 10:44
0
雪    币: 63
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark
2013-9-10 13:41
0
雪    币: 35
活跃值: (139)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
11
mark
2013-9-11 20:23
0
雪    币: 78
活跃值: (74)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
mark
2013-9-11 22:20
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
路过。mark
2013-9-11 22:43
0
雪    币: 91
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark下
2013-9-20 20:34
0
游客
登录 | 注册 方可回帖
返回
//