2020注定是不平凡的一年,前有冠状病毒的肆虐,全国上下都笼罩在一种紧张而又不安的氛围下;因为疫情的严重性,使我被迫享受了学生时代的超长假期。闲来无事,就分析了一下那些静静地躺在我磁盘里的开源软件源码。
gh0st这款软件,主要用途是用于远程操作另一台计算机(包括远程文件的拷贝、远程视频连接等),功能有点类似于QQ中的远程桌面。该软件由两个工程文件组成,分别为gh0st_Client和gh0st_Server,如图1所示:

从gh0st_server开始,启动VS调试,程序运行起来后,中断所有调试(DEBUG——>BreakAll),打开Threads窗口和CallStack窗口,如图2所示:

gh0st_server代表该软件的服务端,服务端启动时开启了10个线程(这和本地处理器个数有关),其中一个为主线程(MainThread),负责注册窗口、创建窗口、初始化线程池与内存池资源及IOCP(完成端口模型);主线程完成资源的初始化,便创建工作线程(ListenThreadProc)并监听客户端的连接请求。

bool CIOCPServer::Initialize(NOTIFYPROC pNotifyProc, CMainFrame* pFrame, int nMaxConnections, int nPort)
{m_pNotifyProc = pNotifyProc;m_pFrame = pFrame;m_nMaxConnections = nMaxConnections;m_socListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (m_socListen == INVALID_SOCKET){TRACE(_T("Could not create listen socket %ld\n"), WSAGetLastError());return false;}// 创建Event事件,用于线程间的同步m_hEvent = WSACreateEvent();if (m_hEvent == WSA_INVALID_EVENT){TRACE(_T("WSACreateEvent() error %ld\n"), WSAGetLastError());closesocket(m_socListen);return false;}// The listener is ONLY interested in FD_ACCEPT// That is when a client connects to or IP/Port// Request async notification/*WSAEventSelect模型是WindowsSockets提供的一个有用异步I/O模型。该模型允许在一个或者多个套接字上接收以事件为基础的网络事件通知。Windows Sockets应用程序在创建套接字后,调用WSAEventSelect()函数,将一个事件对象与网络事件集合关联在一起。当网络事件发生时,应用程序以事件的形式接收网络事件通知。  WSAEventSelect模型简单易用,也不需要窗口环境。该模型唯一的缺点是有最多等待64个事件对象的限制(Linux中SELECT模型最多可以同时处理128个事件对象),当套接字连接数量增加时,就必须创建多个线程来处理I/O,也就是所谓的线程池。*///将m_hEvent事件绑定吧到m_socListen套接字上,用于监听FD_ACCEPT类型的事件int nRet = WSAEventSelect(m_socListen, m_hEvent,FD_ACCEPT);if (nRet == SOCKET_ERROR){TRACE(_T("WSAAsyncSelect() error %ld\n"), WSAGetLastError());closesocket(m_socListen);return false;}SOCKADDR_IN     saServer;// Listen on our designated Port#saServer.sin_port = htons(nPort);// Fill in the rest of the address structuresaServer.sin_family = AF_INET;saServer.sin_addr.s_addr = INADDR_ANY;// bind our name to the socketnRet = bind(m_socListen, (LPSOCKADDR)&saServer, sizeof(struct sockaddr));if (nRet == SOCKET_ERROR){DWORD dwErr = GetLastError();closesocket(m_socListen);return false;}// Set the socket to listennRet = listen(m_socListen, SOMAXCONN);if (nRet == SOCKET_ERROR){TRACE(_T("listen() error %ld\n"), WSAGetLastError());closesocket(m_socListen);return false;}UINT dwThreadId = 0;//创建一个监听线程专门用来监听客户端的连接请求m_hThread =(HANDLE)_beginthreadex(NULL,                // Security0,                   // Stack size - use defaultListenThreadProc,  // Thread fn entry point(void*) this,0,                   // Init flag&dwThreadId);   // Thread addressif (m_hThread != INVALID_HANDLE_VALUE){//初始化IOCP模型InitializeIOCP();m_bInit = true;return true;}return false;
}

因为此时客户端并没有发起连接远程主机的请求,所以主线程创建完工作线程——ListenThreadProc,ListenThreadProc便一直处于while(1)循环中,反复判断m_hkillEvent事件是否有信号(主线程和工作线程之间通过Event事件实现同步)、是否有IO事件的产生。

 unsigned CIOCPServer::ListenThreadProc(LPVOID lParam)
{CIOCPServer* pThis = reinterpret_cast<CIOCPServer*>(lParam);WSANETWORKEVENTS events;while (1){***if (WaitForSingleObject(pThis->m_hKillEvent, 100) == WAIT_OBJECT_0)break;***dwRet = WSAWaitForMultipleEvents(1,&pThis->m_hEvent,FALSE,100,FALSE);//超时的话,则继续下一轮的循环if (dwRet == WSA_WAIT_TIMEOUT)continue;int nRet = WSAEnumNetworkEvents(pThis->m_socListen,pThis->m_hEvent,&events);if (nRet == SOCKET_ERROR){TRACE(_T("WSAEnumNetworkEvents error %ld\n"), WSAGetLastError());break;}if (events.lNetworkEvents & FD_ACCEPT){//如果发生了可读事件,那么就开始调用OnAccept函数执行数据的读取if (events.iErrorCode[FD_ACCEPT_BIT] == 0)pThis->OnAccept();else{TRACE(_T("Unknown network event error %ld\n"), WSAGetLastError());break;}}} return 0;
}

当gh0st_server发出关闭窗口的命令(也即主线程发起退出线程的请求),m_hkillEvent将被设置成受信状态。

void CIOCPServer::Stop()
{::SetEvent(m_hKillEvent);//等待子线程退出,主线程才关闭线程句柄m_thread和事件句柄m_hKillEventWaitForSingleObject(m_hThread, INFINITE);CloseHandle(m_hThread);CloseHandle(m_hKillEvent);
}

当工作线程执行到如下代码处:

 if (WaitForSingleObject(pThis->m_hKillEvent, 100) == WAIT_OBJECT_0)break

等待100毫秒,工作线程退出while循环,如此设计的好处在于:主线程退出,工作线程(子线程)能够及时地进行资源的回收,从而增强了程序的健壮性。
再回头看gh0st_server中IOCPServer这个类,该类是gh0st_server的核心,网络事件的检测、IO请求、网络数据的收发都依赖该类来实现,该类的成员变量和成员方法声明如下:

#define  NC_CLIENT_CONNECT       0x0001
#define NC_CLIENT_DISCONNECT    0x0002
#define NC_TRANSMIT             0x0003
#define NC_RECEIVE              0x0004
#define NC_RECEIVE_COMPLETE     0x0005 // 完整接收
class CLock
{public:CLock(CRITICAL_SECTION& cs, const CString& strFunc){m_strFunc = strFunc;m_pcs = &cs;Lock();}~CLock(){Unlock();}void Unlock(){LeaveCriticalSection(m_pcs);TRACE(_T("LC %d %s\n") , GetCurrentThreadId() , m_strFunc);}void Lock(){TRACE(_T("EC %d %s\n") , GetCurrentThreadId(), m_strFunc);EnterCriticalSection(m_pcs);}
protected:CRITICAL_SECTION* m_pcs;CString               m_strFunc;
};
//声明IO事件
enum IOType
{IOInitialize,IORead,IOWrite,IOIdle
};
class OVERLAPPEDPLUS
{public:OVERLAPPED           m_ol;IOType             m_ioType;OVERLAPPEDPLUS(IOType ioType) {ZeroMemory(this, sizeof(OVERLAPPEDPLUS));m_ioType = ioType;}
};
//客户端context类,包括客户端socket、收发缓冲区
struct ClientContext
{SOCKET             m_Socket;// Store buffersCBuffer                m_WriteBuffer;//自定义的内存池类CBufferCBuffer              m_CompressionBuffer;    // 接收到的压缩的数据CBuffer             m_DeCompressionBuffer;  // 解压后的数据CBuffer                m_ResendWriteBuffer;    // 上次发送的数据包,接收失败时重发时用int                 m_Dialog[2]; // 放对话框列表用,第一个int是类型,第二个是CDialog的地址int                   m_nTransferProgress;// Input Elements for WinsockWSABUF             m_wsaInBuffer;BYTE              m_byInBuffer[8192];// Output elements for WinsockWSABUF             m_wsaOutBuffer;HANDLE               m_hWriteComplete;// Message counts... purely for example purposesLONG               m_nMsgIn;LONG               m_nMsgOut;  BOOL                m_bIsMainSocket; // 是不是主socketClientContext*        m_pWriteContext;ClientContext*      m_pReadContext;
};
template<>
inline UINT AFXAPI HashKey(CString & strGuid)
{return HashKey( (LPCTSTR) strGuid);
}
#include "Mapper.h"
typedef void (CALLBACK* NOTIFYPROC)(LPVOID, ClientContext*, UINT nCode);
typedef CList<ClientContext*, ClientContext*& > ContextList;
class CMainFrame;
class CIOCPServer
{public:void DisconnectAll();CIOCPServer();virtual ~CIOCPServer();NOTIFYPROC                 m_pNotifyProc;CMainFrame*                   m_pFrame;bool Initialize(NOTIFYPROC pNotifyProc, CMainFrame* pFrame,  int nMaxConnections, int nPort);//标准的windows线程函数的申明,加static就是为了使得线程函数不能有类的this指针,因为线程函数是按照stdcall的方式进行调用static unsigned __stdcall ListenThreadProc(LPVOID lpVoid);static unsigned __stdcall ThreadPoolFunc(LPVOID WorkContext);static CRITICAL_SECTION  m_cs;void Send(ClientContext* pContext, LPBYTE lpData, UINT nSize);void PostRecv(ClientContext* pContext);bool IsRunning();void Shutdown();void ResetConnection(ClientContext* pContext);LONG                   m_nCurrentThreads;LONG                  m_nBusyThreads;UINT                 m_nSendKbps; // 发送即时速度UINT                  m_nRecvKbps; // 接受即时速度UINT                  m_nMaxConnections; // 最大连接数
protected:void InitializeClientRead(ClientContext* pContext);BOOL AssociateSocketWithCompletionPort(SOCKET device, HANDLE hCompletionPort, DWORD dwCompletionKey);void RemoveStaleClient(ClientContext* pContext, BOOL bGraceful);void MoveToFreePool(ClientContext *pContext);ClientContext*  AllocateContext();LONG               m_nWorkerCnt;bool               m_bInit;bool                m_bDisconnectAll;BYTE               m_bPacketFlag[5];void CloseCompletionPort();void OnAccept();bool InitializeIOCP(void);void Stop();ContextList               m_listContexts;ContextList              m_listFreePool;WSAEVENT             m_hEvent;SOCKET                 m_socListen;    HANDLE                  m_hKillEvent;HANDLE                 m_hThread;HANDLE                    m_hCompletionPort;bool                  m_bTimeToKill;CCpuUsage             m_cpu;LONG                  m_nKeepLiveTime; // 心跳超时// Thread Pool TunablesLONG                 m_nThreadPoolMin;LONG                   m_nThreadPoolMax;LONG                   m_nCPULoThreshold;LONG                  m_nCPUHiThreshold;CString GetHostName(SOCKET socket);void CreateStream(ClientContext* pContext);BEGIN_IO_MSG_MAP()IO_MESSAGE_HANDLER(IORead, OnClientReading)IO_MESSAGE_HANDLER(IOWrite, OnClientWriting)IO_MESSAGE_HANDLER(IOInitialize, OnClientInitializing)END_IO_MSG_MAP()bool OnClientInitializing    (ClientContext* pContext, DWORD dwSize = 0);bool OnClientReading       (ClientContext* pContext, DWORD dwSize = 0);bool OnClientWriting       (ClientContext* pContext, DWORD dwSize = 0);
};

当有网络事件产生时,如何去收发数据、如何解包等关键业务逻辑将在下一节中进行讲解。

关于一款开源远程控制软件(gh0st)的源码分析(一)相关推荐

  1. (4.2.40)阿里开源路由框架ARouter的源码分析

    一需求背景 1 Android原生方案的不足 2 自定义路由框架的适用场景 3 对自定义路由框架的设想 二ARouter的概述 三ARouter的引入和使用 四源码分析 1 arouter-annot ...

  2. 几款小众web指纹识别工具源码分析

    公粽号:黒掌 一个专注于分享网络安全.黑客圈热点.黑客工具技术区博主! Webfinger 简介 这是一款很小巧的工具,由Python2编写,使用Fofa的指纹库 Github地址:https://g ...

  3. Piwigo 一款开源的图片管理系统PHP源码

    介绍: Piwigo是一个PHP开源图片管理系统,系统拥有多款主题风格,提供200多种插件和主题,可以直接通过网站后台安装,类似于Wordpress可以在后台安装插件.主题.管理用户.选项设置等等! ...

  4. 多款开源免费网络相册/相册系统源码推荐

    Flickr和Picasa是目前互联网两家知名的免费照片储存.分享的网站.当然你也可以拥有一个类似的网站,笔者今天整理并推荐几款国内外开源的相册源码. Gallery Gallery 是国外一个免费开 ...

  5. vs2008编译QT开源项目--太阳神三国杀源码分析(一) 项目编译及整体分析

    请参看 http://tieba.baidu.com/f?kz=1508964881 按照上面的网址教程,下载三国杀源码,swig工具,并下载最新的QT4.8.2 for vs2008.我本机已经安装 ...

  6. Ciclop开源3D扫描仪软件---Horus源码分析之Image_capture.py

    *                                                 联系方式:  *                                           ...

  7. Miracle密码算法开源库(一)源码分析 :mraes.c

    2021SC@SDUSC 山东大学软件学院软件工程应用与实践 一.mraes.c结构 mraec.c的总体结构如下,具有fbsub.ftable1等数组,主要实现了aes_decrypt .aes_e ...

  8. 开源中国 OsChina Android 客户端源码分析(7)二维码生成对话框

    为什么80%的码农都做不了架构师?>>>    功能描述:主界面中点击"我"进入个人中心, 点击右侧二维码图标,弹出附有个人信息的二维码对话框. 1源码中的布局文 ...

  9. 开源中国android代码是什么,开源中国 OsChina Android 客户端源码分析(1)启动界面 app_start...

    1启动界面的布局文件为app_start.xml ,对应的类文件为net.oschina.app 包下的AppStart.java. 2对于布局文件而言,因为只显示一张主题图片,因此布局简单直接设置背 ...

最新文章

  1. html h 不换行,css 强制不换行
  2. 设置默认settings文件_Django 学习笔记系列 之 settings.py 设定
  3. 网站的次导航是什么?对网站优化有什么好处?
  4. php个人扫码支付,PHP个人发卡网源码,支持MA支付对接,扫码自动发货
  5. AliOS Things 基于组件化思想的多bin特性
  6. 浅谈NB-IoT应用场景及方案
  7. 关于linux拨号上网的软件实现方案[原创]
  8. java并发编程实战学习(3)--基础构建模块
  9. 大前端时代下,如何成为一名优秀的程序员?
  10. Appium移动自动化测试-----(一)Appium介绍
  11. 网络规划---网络计划图的时间参数计算
  12. java虚拟机带键盘安卓版下载。_jvm1.5官方下载
  13. 手机电子词典_如何把手机变成一款英语学习神器?
  14. 电脑蓝屏分析教程,附工具WinDbg(x86 x64)6.12.0002.633下载
  15. 郭天祥 十天搞定单片机 (2)流水灯+蜂鸣器+调试
  16. Java 网络编程:(七)UDP网络编程
  17. CSAPP HITICS 大作业 hello's P2P by zsz
  18. Elasticsearch之Mapping设置详解
  19. Android imagebutton美化+edittext美化 实现登录界面美化
  20. 影评分析第2篇 《博人传-火影忍者新时代》透过2W条评论看动漫

热门文章

  1. 如何在局域网内实现文件夹共享
  2. 中国没有掌握的尖端技术_适用于所有人的尖端AAA遮光技术
  3. List 列表的用法
  4. Unity Shader:实现菲涅尔+色散效果以及相关原理解析
  5. 追洞小组 | Windows安装Immunity CANVAS教程
  6. 苹果退款_苹果ios退款流程最新苹果内购退款政策条例
  7. 语音信号的录制和处理
  8. 洛谷P1851 好朋友
  9. 无缝滚动--基本实现
  10. java web热区链接_HTML图片热区map area的用法