網路遊戲逆向分析-3-通過發包函數找功能call

網路遊戲逆向分析-3-通過發包函數找功能call

網路遊戲和單機遊戲的分析有相似點,但是區別還是很大的。

網路遊戲和單機遊戲的區別:

網路遊戲是需要和伺服器進行交互的,網遊中的所有功能幾乎都會先發送封包數據到伺服器,然後有伺服器做出判斷後回饋給客戶端,客戶端才會產生對應的相關功能。

 

找功能call的辦法:

由於網遊和單機遊戲的區別,所以在網路遊戲中要尋找功能call可以通過在發包函數處下斷點來回溯找功能call。

相當於伺服器是一個皇帝,客戶端每想幹什麼事情前都得先給伺服器進行交互,直到伺服器同意才行。

大概是以下這個邏輯:

 

 

那麼通過發包函數往上找,就可以找到可能是真實的吃藥函數,當然也可能是吃藥函數到發包函數的中間過程,但是沒有關係能調用就好。

發包函數:

Windows的socket發包函數總共有三個:

//ws2_32.send
int WSAAPI send(
SOCKET     s,
const char *buf,
int       len,
int       flags
);
int WSAAPI WSASend(
SOCKET                             s,
LPWSABUF                           lpBuffers,
DWORD                             dwBufferCount,
LPDWORD                           lpNumberOfBytesSent,
DWORD                             dwFlags,
LPWSAOVERLAPPED                   lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
int WSAAPI sendto(
SOCKET         s,
const char     *buf,
int           len,
int           flags,
const sockaddr *to,
int           tolen
);

尋找喊話的功能call

首先查找發包函數,然後給發包函數打斷點後再喊話,來查看是調用的哪一個發包函數。

這裡通過查找後,發現只有send是可以斷下來的,WSASend和sendto都沒有用。

但是這裡的send和我們想的不一樣:

 

 

它打上斷點後一直就卡這裡,還沒有喊話就斷在這裡了。

這裡就需要引入一個新的內容:心跳包。

心跳包:

心跳包顧名思義,字面意思是按照心跳來發包,實際意思就是每時每刻都給伺服器發個包,藉此伺服器可以用來檢測,比如你是否還在線,是否掉線了,或者你有沒有干別的壞事之類的,時刻讓客戶端和伺服器保持聯繫。

心跳包的特點:心跳包只需要有特定的驗證字元就好了,不需要發一堆東西來浪費資源,所以心跳包的長度len一般都比較小,而且長度是固定的。

 

還好這裡心跳包頻率不快,還是會隔一會才心跳的,還是可以按照之前的邏輯來找函數call。

假設:A調用B,B調用C,C調用發包函數,那麼發包函數執行到函數返回再往上一條的call指令就是call的發包函數,然後再在調用發包函數這裡使用執行到函數返回再往上一個指令就是call調用C函數,這樣一直往上找六到七個函數來先分析一下看看

這裡採用xdbg的辦法來給每個函數上注釋,通過找到發包函數,然後運行到函數返回回到上一層,然後這樣來實現往上找函數。

 

 

 

通過xdbg的視圖->注釋可以查看到自己添加的注釋的位置,以此來方便查看。

通過從send 到1然後到8從下往上給函數打斷點,打完斷點後人物再喊話來找到函數的call函數。(可以在注釋這個窗口打斷點)

打完斷點後還得測試,測試是否是只有喊話的時候才會停下來,否則就不能用,這裡我是在對應注釋3這個函數找到的唯一可以在喊話後斷下來的函數。

找到喊話功能call函數後進行分析

前面我們找到了喊話的函數,現在對其進行分析,一個函數必不可少的有,返回類型,函數參數的結構。返回類型不太好說明,但是函數參數可以先考慮,首先分析函數參數:

 

 

進入這個函數查看,最後有一個ret10,而且前面也給開闢的堆棧平衡了,說明函數的參數棧空間有十六位數的 10個,就是十位數的16,在看該函數前面有四個push,說明就是有四個參數了。

 

 

分析這四個參數來解剖該函數:

首先這裡有4個push和一個mov ecx,eax。前面在學習反彙編C++的時候就得注意這個ecx,ecx很有可能是類裡面的this指針,所以這次給第一個push edx打斷點然後一直分析到調用函數:

edx == 00000000
ebx == 00000000
ecx == 字元串的首地址
edx == 6A1E8600
ecx == 2334FD68
//前面的edx ebx ecx edx都是入棧,ecx應該是一個對象的值把,這裡猜測一下。
//然後再多調用幾次看看有啥變化
//結論是不管多少次都只有ecx == 字元串首地址這個值做了變化

這裡先利用程式碼注入器試一下:

注意:這裡需要在一個空白的地方先放置字元串的數據,因為一會要用到字元串的首地址:

 

 

 

但是跟我們輸入的字元串不一樣,很有可能是編碼的問題,這裡改成unicode試試:

 

 

這樣就ok了。

但是這個同樣也只能臨時用一下,所以還得往上找基址。

需要找的只有edx和ecx,因為別的都是0,或者是記憶體里的字元串,還有一個就是call的地址是一個靜態地址也不用找。

就用前面的教程繼續找edx和ecx的基址://www.cnblogs.com/Sna1lGo/p/14897870.html