1. 程式人生 > >gh0st遠控原始碼圖文詳解Gh0st通訊協議解析(1)

gh0st遠控原始碼圖文詳解Gh0st通訊協議解析(1)

與大家分享下gh0st通訊的全過程解析。瞭解gh0st的通訊上線發包全過程,網上有很多相關資料,自己在總結了下。希望對初學者有幫助少走彎路,gh0st的核心是個不錯的經典值得學習。轉載請註明: 速維網路

Gh0st通訊協議解析(1)  gh0st遠控原始碼釋出至今已有不少關於gh0st的改寫教程,gh0st分析教程,gh0st的功能增加修改等相關資料網上多的數不勝數,今天我把我自己學的總結給大家。需要打造一款屬於自己的遠控那就必須知道遠控的核心怎麼寫的,C/S都有那些通訊過程。我們以gh0st為例,對gh0st的通訊協議進行一個完整的解析,看看gh0st這款遠控的核心技術的來龍去脈。
從主控端初始化IOCP伺服器開始講起程式碼位置:gh0st.cpp【BOOL CGh0stApp::InitInstance()】
  1.         // 啟動IOCP伺服器
  2.         int        nPort = m_IniFile.GetInt("Settings", "ListenPort");
  3.         int        nMaxConnection = m_IniFile.GetInt("Settings", "MaxConnection");
  4.         if (nPort == 0)
  5.                 nPort = 80;
  6.         if (nMaxConnection == 0)
  7.                 nMaxConnection = 10000;
  8.         
  9.         if (m_IniFile.GetInt("Settings", "MaxConnectionAuto"))
  10.                 nMaxConnection = 8000;

  11.         ((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection);
IOCP伺服器是在CGh0stApp::InitInstance這個函式中被呼叫的,實際上是呼叫了CMainFrame的一個成員函式:CMainFrame::Active。看看這個函式都做了哪些事情。
跳轉到:MainFrm.cpp 往下看可以看到如下程式碼
  1. void CMainFrame::Activate(UINT nPort, UINT nMaxConnections)
  2. {
  3.         CString                str;

  4.         if (m_iocpServer != NULL)
  5.         {
  6.                 m_iocpServer->Shutdown();
  7.                 delete m_iocpServer;

  8.         }
  9.         m_iocpServer = new CIOCPServer;

  10.         // 開啟IPCP伺服器
  11.         if (m_iocpServer->Initialize(NotifyProc, this, 100000, nPort))
  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.                   m_wndStatusBar.SetPaneText(0, str);
  27.                 str.Format("埠: %d", nPort);
  28.                 m_wndStatusBar.SetPaneText(2, str);
  29.         }
  30.         else
  31.         {
  32.                 str.Format("埠%d繫結失敗", nPort);
  33.                 m_wndStatusBar.SetPaneText(0, str);
  34.                 m_wndStatusBar.SetPaneText(2, "埠: 0");
  35.         }

  36.         m_wndStatusBar.SetPaneText(3, "連線: 0");
  37. }
首先判斷這個m_iocpServer全域性變數是否已經指向了一個CIOCPServer,如果是的話,就要先關閉它,並且刪除掉這個CIOCPServer所佔的記憶體空間。到這裡我有些猶豫要不要去追蹤這個CIOCPServer::Shutdown函式呢,一方面,如果去追蹤的話,我們將不得不深入到CIOCPServer這個類裡面去抽絲剝繭,尋找Shutdown函式,以及這個函式內部所呼叫的一系列的函式,這樣我不得不深度遍歷整個呼叫過程。另一方面,如果追蹤吧,會使得這篇文章的主線凌亂掉,如果不追蹤吧,我自己會凌亂掉,思前想後,寧可讓大家瘋掉,也不能讓我瘋掉,因為我瘋掉了就寫不出這個分析文章了……
好了,我們接下來看看這個CIOCPServer::Shutdown這個函式內部的一個執行邏輯。下面程式碼位置是在:IOCPServer.cpp
  1. void CIOCPServer::Shutdown()
  2. {
  3.         if (m_bInit == false)
  4.                 return;

  5.         m_bInit = false;
  6.         m_bTimeToKill = true;

  7.         // Stop the listener
  8.         Stop();


  9.         closesocket(m_socListen);        
  10.         WSACloseEvent(m_hEvent);


  11.         CloseCompletionPort();
  12.         
  13.         DeleteCriticalSection(&m_cs);

  14.         while (!m_listFreePool.IsEmpty())
  15.                 delete m_listFreePool.RemoveTail();

  16. }
先來看看這個CIOCPServer::m_bInit這個變數。這個變數有什麼作用呢,看這些個地方:
1:在CIOCPSserver的建構函式中有這麼一句:m_bInit = false;
2:在CIOCPServer::Initialize這個函式中有這麼一句:m_bInit = true;
3:在CIOCPServer::Shutdown這個函式中有這麼一句:m_bInit = false;
4:在CIOCPServer::IsRunning這個函式中有這麼一句:return m_bInit;
從以上的各個地方我們可以知道,這個m_bInit就是一個記錄CIOCPServer這個伺服器的執行狀態的,它只有兩個意思:開啟或者關閉。當CIOCPServer伺服器初始化完畢的時候,此值將會被設為true。在關閉的時候,此值將會被設為false,其它的時候此值作為CIOCPServer執行狀態的一個考量。
首先判斷這個m_binit是否為false,如果為false那CIOCPServer本身就沒有開啟,還關閉個屁,如果為true的話,接下來就將其執行狀態設為false。

然後看看CIOCPServer::m_bTimeToKill這個變數。這個變數的作用:
1:在CIOCPSserver的建構函式中有這麼一句:m_bTimeToKill = false;
2:在CIOCPServer::Shutdown這個函式中有這麼一句:m_bTimeToKill = true;
3:在核心的完成埠迴圈中有這麼一句:
for (BOOL bStayInPool = TRUE; bStayInPool && pThis->m_bTimeToKill == false; )
可以看出,m_bTimeToKill這個值就是一個記錄是否結束掉在完成埠的所有等待執行緒的這麼一個哨兵值,如果該值被設定為true,那麼所有等待在完成埠上的執行緒都將結束並退出,這樣,就不會再有工作執行緒來處理這個完成埠上的所有Read、Write、Initize的請求,在這裡要明白一點:工作執行緒數是跟執行主控端的機子上的核心數相關的。

接下來,我們程式的流程就到了CIOCPSserver::Stop這個函式裡了,這個函式的一個主要的功能就是結束掉這個監聽的socket,已達到後續的連線請求不再受理。我們看看在這個函式裡CIOCPServer都做了哪些處理:
  1. void CIOCPServer::Stop()
  2. {
  3.     ::SetEvent(m_hKillEvent);
  4.     WaitForSingleObject(m_hThread, INFINITE);
  5.         CloseHandle(m_hThread);
  6.     CloseHandle(m_hKillEvent);
  7. }
首先來看看這個CIOCPServer::m_hKillEvent這個變數的用途
1:在CIOCPServer的建構函式中有這麼一句呼叫
   m_hKillEvent    = CreateEvent(NULL, TRUE, FALSE, NULL);在建構函式中建立了一個人工置信、初始狀態為未受信的、未命名的事件物件。
2:在CIOCPServer::L istenThreadProc這個函式中有這麼一句呼叫
   if (WaitForSingleObject(pThis->m_hKillEvent, 100) == WAIT_OBJECT_0)
     break;
   在監聽上線的執行緒中的無限迴圈中,這一句的作用就是,當m_hKillEvent受信的時候,這個無限迴圈結束
3:在一個關鍵的地方,就是在CIOCPServer::Stop中SetEvent(m_hKillEvent)的呼叫。
通過以上的分析我們可以看出,這個變數存在的意義,就是承擔得起當要關閉CIOCPServer這個伺服器的時候,使得監聽執行緒結束監聽這麼一個功能。

再來看看這個CIOCPServer::m_hThread這個變數的用途1:在CIOCPServer::Initialize函式中,有以下的這個呼叫
   m_hThread =(HANDLE)_beginthreadex(NULL,                         // Security
                                                                0,                               // Stack size - use default
                                                                ListenThreadProc,  // Thread fn entry point
                                                                (void*) this,     
                                                                0,                               // Init flag
                                                                &dwThreadId);    // Thread address
2:在CIOCPServer::Stop中,有WaitForSingleObject(m_hThread, INFINITE)的呼叫。
從以上分析我們可以看出這個變數的用處就是作為監聽執行緒的一個控制代碼而存在的,它代表了這個監聽執行緒實體。

至此,這個CIOCPServer::Stop函式我們就分析完了,回溯到CIOCPServer::Shutdown函式繼續看。
首先,我們看看CIOCPServer::m_socListen這個變數
1:在CIOCPServer::Initialize這個函式中,被賦值為監聽套接字控制代碼,並且被設定為僅僅對網路事件FD_ACCEPT感興趣,然後就是繫結本機,開始監聽。在這裡我想提一點,這個監聽套接字並沒有關聯到後續建立的完成埠上,關聯到這個完成埠上的都是那些跟主控端建立了連線的那些套接字。在這些套接字上如果發生了什麼網路事件,則由完成埠上的訊息派遣函式去完成。
關於這個完成埠的執行機理,在這裡就不表了,後面會有詳細的介紹,這裡只提到一點,這點是關於完成埠的大體的架構問題:建立一個完成埠+設定一個專門為這個完成埠服務的N個執行緒,不斷的去查詢完成埠上是否有讀、寫的這些請求+多個向此完成埠上投遞讀取或者寫入請求的操作+真正的去處理這些讀、寫請求的操作。以上就是一個大概的完成埠執行情況。
2:在CIOCPServer::L istenThreadProc這個監聽執行緒中,有以下這麼個操作
     int nRet = WSAEnumNetworkEvents(pThis->m_socListen,
                                                       pThis->m_hEvent,
                                                       &events);
   從發生在m_sockListen這個套接字上的網路事件中選擇一個我們自己感興趣的網路事件進行處理,因為先前的時候,我們已經註冊了在m_sockListen上感興趣的網路事件,即FD_ACCEPT這麼個網路事件。因此在這裡我們只對發生在這個套接字上的這個網路事件進行處理。
3:在CIOCPServer::OnAccept這個函式中,有以下這麼個操作
   clientSocket = accept(m_socListen,
                                (LPSOCKADDR)&SockAddr,
                                   &nLen);
   在這裡就是對發生在這個套接字上的連線請求進行了一個接受連線的那麼一個處理。
通過以上的分析,我們可以得出這麼個結論,這個m_socListen就是一個監聽套接字控制代碼。

接下來,我們對CIOCPServer::m_hEvent這個變數進行一個分析
1:在CIOCPServer::Initialize這個函式中,對這個變數進行了以下這麼個操作
   m_hEvent = WSACreateEvent();
   進行了一個賦值的操作,建立的是一個自動重置、非受信的一個事件物件。接下來是
   int nRet = WSAEventSelect(m_socListen,
                                            m_hEvent,
                                            FD_ACCEPT);
   將這個事件物件與m_socListen套接字相關聯。當在m_socListen上發生了FD_ACCEPT這個網路事件的時候,這個物件將會被置信。
2:在CIOCPServer::L istenThreadProc這個函式中,對這個變數有以下這麼個操作
   dwRet = WSAWaitForMultipleEvents(1,
                                                         &pThis->m_hEvent,
                                                         FALSE,
                                                         100,
                                                         FALSE);
   等待這個事件物件被置信。
     int nRet = WSAEnumNetworkEvents(pThis->m_socListen,
                                                  pThis->m_hEvent,
                                                  &events);
   當這個事件物件受信的時候,枚舉發生在這個事件物件上的網路事件,並判斷是否為期望的網路事件發生。
3:在CIOCPServer::Shutdown這個函式中對這個變數有這個操作
     WSACloseEvent(m_hEvent);
通過以上分析,我們可以得出這麼個結論,這個m_hEvent的存在就是為了給這個監聽套接字做一個事件表象,就是說所有發生在這個套接字上的事件,都是由這個事件物件來反映出來。

接下來我們需要看這個函式:CIOCPServer:: CloseCompletionPort,看看在這個函式中都進行了哪些操作。

首先,先看看CIOCPServer::m_nWorkerCnt這個變數。
1:在CIOCPServer::InitializeIOCP這個函式裡,對這個變數有這麼個操作
  
可以看到,這個值是代表了,為完成埠服務的執行緒的數量。
2:在CIOCPServer::ThreadPoolFunc函式中,對這個變數有這麼個操作
   InterlockedDecrement(&pThis->m_nWorkerCnt);
   這個操作是在某個為完成埠服務的執行緒即將結束的時候的一個操作
3:在CIOCPServer::CloseCompletionPort函式中,對這個變數的操作就不多少了
通過以上的分析,我們可以得出這樣一個結論:這個m_nWorkerCnt的值就是代表了為完成埠服務的執行緒數量,因為此值是全域性變數,因此各個執行緒對此值的訪問時共享的,必須對此值的訪問進行一個同步處理—原子操作。

接下來,我們重點看看這個很有意思的迴圈
while (m_nWorkerCnt)
{
       PostQueuedCompletionStatus(m_hCompletionPort, 0, (DWORD) NULL, NULL);
       Sleep(100);
}
為什麼說這個迴圈有意思呢,因為這個迴圈的這個迴圈體是給每一個為這個完成埠服務的執行緒傳送一條結束自身的指令。它是如何實現的呢?且聽我慢慢的道來……

首先,在CIOCPServer::ThreadPoolFun這個服務執行緒中,有這麼一塊程式碼

   這個for迴圈的結束條件有兩個,一個是m_bTimeToKill被置為True,再一個是bStayInPool被置為False.第一種條件在前面我們已經討論過了,現在討論第二種情況,在什麼情況下bStayInPool會被置為False?
   本人所述以下內容因為跟作者的原始碼有出入,不敢苟同原作者,但是有沒有好的證據來證明我的正確性。因此,我對以下紅字部分的內容不負責,有錯誤之處請諒解…………

     以上程式碼就是我認為作者邏輯極度混亂的地方。分析這塊程式碼的上下文我們可以發現,這段程式碼的用意就是對GetQueuedCompletionStatus這個函式的返回值以及引數輸出值進行一個分類判斷,並加以不同的處理邏輯。
   1:首先,是如果傳輸資料的過程中出現了錯誤,那應該如何處理
   2:資料正常傳輸的情況下,根據CPU的使用率進行一個自適應執行緒調整
     3:資料正常傳輸的情況下,由完成埠的派遣例程處理相關的請求操作
     問題,就出現在這第二種情況,我猜測原作者是將自適應調整執行緒中的結束執行緒與我們上面討論的為關閉掉完成埠而向工作執行緒傳送結束指令的這兩個操作合二為一處理。但是依愚兄之見,這兩個操作還真不能夠放到一起去,應該分開處理。按照我的邏輯,應該再新增一種情況的處理:

相關推薦

gh0st原始碼圖文Gh0st通訊協議解析1

與大家分享下gh0st通訊的全過程解析。瞭解gh0st的通訊上線發包全過程,網上有很多相關資料,自己在總結了下。希望對初學者有幫助少走彎路,gh0st的核心是個不錯的經典值得學習。轉載請註明: 速維網路 Gh0st通訊協議解析(1)  gh0st遠控原始碼釋出至今已有不少

Fragment之三——管理Fragment1

前言:follow your heart,be your own king相關文章:前面給大家稍微看了要怎麼使用fragment,在上篇中,我們也初步接觸到了add,replace這些fragment操作的函式,下面就再詳細講講如何管理Fragment頁面吧。一、概述 1、F

gh0st通訊協議解析2

Gh0st通訊協議解析(2) 從被控端主動去連線主控端開始談起。世間萬事萬物有始有終,宇宙環宇的動力起點就是上帝的那一推之力。當然,主控端與被控端的互動總是從被控端主動連線到主控端開始的,讓我們從發起連線這個引爆點談起…… **********************

Spring IOC原理原始碼解析(@Autowired原理 :標識建構函式)

IOC,inversion of control 控制反轉,有時候也叫DI,dependency injection 依賴注入,是一種程式碼解耦的方式。 在一個類中定義一個屬性,正常情況下需要在此類中有對此屬性賦值的程式碼,如setter方法,或者在建構函式中

OSI七層之四 傳輸層Transport

http 計算機 地址 包括 分組 tcp aik 全部 滿足 一、簡介   第四層的數據單元也稱作數據包(packets)。但是,當你談論TCP等具體的協議時又有特殊的叫法,TCP的數據單元稱為段(segments)而UDP協議的數據單元稱為“數據報(datagrams)

javascript 中的比較==和===

不一致 mit 如果 asc onu tin 算法 復雜 undefine 抽象相等比較算法 比較運算 x==y, 其中 x 和 y 是值,產生 true 或者 false。這樣的比較按如下方式進行: 若 Type(x) 與 Type(y) 相同, 則 若 Type(x)

串口驅動程序設計---串口初始化

flag more ini board 幾分鐘 cor configure 設計 rom 串口驅動程序設計詳解---串口初始化(上) 原創 2016年05月19日 23:51:13 標簽: 串口驅動初始化流程 / 內核源碼分析 / linux / ARM / 架構

C#特性和反射

typeinfo ref 都是 system.in 全局 color com 依然 程序   類型信息(Type Information)用來表示類型聲明的信息,通過抽象基類System.Type的實例存儲這些信息,當使用反射時,CLR獲取指定類型的Type對象,通過這個對

通過,認識遞迴recursion

首先先對遞迴進行入門。 遞迴是以自相似的方式重複專案的過程。在程式語言中,如果程式允許您在同一函式內呼叫函式,則稱其為函式的遞迴呼叫。 簡而言之,遞迴就是函式的自身呼叫。可以看看下面的遞迴使用: void Recursive() { Recursive();//call itself

C#委託和事件

  一、當我們使用關鍵字delegate宣告一個自定義委託型別時,實際上是聲明瞭一個該名稱的類型別,繼承自抽象類System.MulticastDelegate,還包含例項方法Invoke、BeginInvoke、EndInvoke:   public delegate void MyDelegate

JavaScript正則表示式

本文是JavaScript正則表示式的第三篇文章,若是對正則表示式陌生的話,可以看我之前的兩篇文章。 詳解正則表示式(一) 詳解正則表示式(二) 貪婪模式和非貪婪模式 1、貪婪模式 在匹配成功的情況下,儘可能多的匹配。而JavaScript預設的就是貪婪模式。話不多說,直

JavaScript正則表示式

RegExp 物件表示正則表示式,它是對字串執行模式匹配的強大工具 這篇文章主要是對正則表示式有一個全面的瞭解,學完之後,當再次看到一些比較複雜的正則表示式的時候就可以逐步分析了。當然,再加上一段時間的練習,相信你自己也可以學會怎麼使用正則表示式的。 1、例項化正則表示式的兩種方

資料庫 表空間以及其使用方法 Oracle

表空間 表空間是資料庫中最大的邏輯單位,Oracle資料庫採用表空間將相關的邏輯元件組合在一起,一個Oracle資料庫至少包含一個表空間。每個表空間由一個或多個數據檔案組成,一個數據檔案只能與一個表空間相聯絡。 在每一個數據庫中都有一個名為SYSTEM的表空間,即系統表空間,該表空間是在

【Android 動畫】動畫之補間動畫

前言 :之前很早就想寫寫Android 的動畫,最近剛好有時間,大概聊一聊安卓動畫。 我個人習慣將動畫分為:補間動畫(透明度、旋轉、位移、縮放)、幀動畫、和屬性動畫,這一篇,我們先說說補間動畫。 補間動畫這個詞出於flash,在兩個關鍵幀(可以理解成動畫開始和結束)中間需要做“補

【Android 動畫】動畫之插值器

大家好,在上一篇中,我們介紹了Android 的補間動畫,這一篇我們來說說動畫的另外一個公共屬性插值器Interpolator 【Android 動畫】動畫詳解之補間動畫(一) 在上一節中,實現的旋轉、位移動畫等動畫,我們會發現它一直是勻速的,但如果我們需要做一個加

Spring Boot配置檔案-ConfigurationProperties和Value優缺點-好文

文章轉自 http://www.cnblogs.com/itdragon/p/8686554.html Spring Boot提供了兩種常用的配置檔案,分別是properties檔案和yml檔案。他們的作用都是修改Spring Boot自動配置的預設值。相對於properties檔案而言,yml檔

GitHub使用教程——官網操作指南翻譯

GitHub使用指南 原文地址:GitHub官網 示例專案:Hello World 十分鐘輕鬆教學 在學習計算機語言程式設計的過程中建立Hello World 專案是一個歷史悠久的傳統。當你接觸一門新事物的時候可以用它來做一個簡單的練習。讓我們開始使用github吧! 通過本文,

Immutable 及 React 中實踐 轉載

轉載自:https://zhuanlan.zhihu.com/p/20295971,今天看到這篇文章後情不自禁的轉載過來了,真的非常值得收藏的一篇文章 Shared mutable state is the root of all evil(共享的可變狀態是萬惡之源) -- Pete Hunt

【C++】行內函數inline

前言 最近在學習C++的時候,行內函數讓我很迷糊,上網查閱了很多的資料,發現裡邊解釋的很抽象,最後在B站裡將行內函數理解了!如果你想要搞懂行內函數,那麼一定要好好看看此篇部落格! 1、什麼是行內函數 行內函數(有時稱作線上函式或編譯時期展開函式)是一種程式語言結構,用來建議編

由Leetcode演算法 之 動態規劃DP

因為最近一段時間接觸了一些Leetcode上的題目,發現許多題目的解題思路相似,從中其實可以瞭解某類演算法的一些應用場景。 這個隨筆系列就是我嘗試的分析總結,希望也能給大家一些啟發。 動態規劃的基本概念 一言以蔽之,動態規劃就是將大問題分成小問題,以迭代的方式求解。 可以使用動態規劃求解的問題