您好,登錄后才能下訂單哦!
?引子:
? 早些時(shí)候想去研究Windows Filter Platform (WFP),參考資料少且不齊全。貼吧、論壇搜集一些關(guān)于網(wǎng)絡(luò)過(guò)濾、網(wǎng)絡(luò)監(jiān)聽(tīng)的工具。開(kāi)始琢磨別人是怎樣寫(xiě),怎樣實(shí)現(xiàn)的。然而沒(méi)有去研究驅(qū)動(dòng)層(很多原理性的東西需要時(shí)間),自己寫(xiě)用戶層前一直琢磨,三環(huán)如何去實(shí)現(xiàn)這些網(wǎng)絡(luò)監(jiān)聽(tīng)?用什么API可以實(shí)現(xiàn)對(duì)數(shù)據(jù)包的捕獲呢?怎樣把這些數(shù)據(jù)進(jìn)行處理?
? 當(dāng)我看到其中的項(xiàng)目的時(shí)候,單純的.exe文件,運(yùn)行后也沒(méi)有釋放dll之類(lèi)的動(dòng)態(tài)資源,腦海中出現(xiàn)一個(gè)念頭shellCode(這里就先叫shellCode了,其實(shí)準(zhǔn)確說(shuō)是機(jī)器碼)。這個(gè)程序是好多年前的,比較單一,注入任意進(jìn)程,捕獲網(wǎng)絡(luò)響應(yīng)數(shù)據(jù),兼容性也還不錯(cuò),用360瀏覽器做測(cè)試,windows7~windwos10網(wǎng)絡(luò)響應(yīng)捕獲正常。
?這是給大家提供一些逆向的思路,并不是教程系列,有一定逆向基礎(chǔ)才可以(對(duì)匯編、網(wǎng)絡(luò)編程、OD等工具了解)。當(dāng)遇到類(lèi)似的程序或者問(wèn)題,對(duì)他們的實(shí)現(xiàn)原理做到心中有度。
?逆向分析目錄:
1、注入代碼分析 | 2、shellCode調(diào)試方法 | 3、shellCode動(dòng)態(tài)分析 |
---|---|---|
--------★ ★ ★------- | -----------------★ ★ ★------------------- | -----------★ ★ ★ ★----------- |
?草稿示意圖:
??????????????????圖片二:程序流程草圖
一、?注入代碼分析:
?? 用IDA先簡(jiǎn)略的瀏覽一下匯編指令,發(fā)現(xiàn)反匯編代碼不算多,了解了基本的程序結(jié)構(gòu),拖到OD開(kāi)始動(dòng)態(tài)調(diào)試。
?? 如圖二中第一步所示,獲取被注入的數(shù)據(jù),需要獲取選中的目標(biāo)進(jìn)程Id等,并且OpenProcess打開(kāi)目標(biāo)進(jìn)程,獲取句柄才可以完成注入,如下圖所示(圖中關(guān)鍵代碼已給出解釋?zhuān)?br/>
??????????????????圖片三:獲取目標(biāo)進(jìn)程信息及獲取句柄
?? 目標(biāo)進(jìn)程申請(qǐng)?zhí)摂M內(nèi)存,如下圖所示:
??????????????????圖片四:申請(qǐng)?zhí)摂M內(nèi)存空間
?? 目標(biāo)進(jìn)程虛擬內(nèi)存申請(qǐng)之后,寫(xiě)入shellCode,且5次寫(xiě)入目標(biāo)程序申請(qǐng)的虛擬內(nèi)存空間,這個(gè)地方我們無(wú)需關(guān)系寫(xiě)入shellCode的內(nèi)容及作用,后面會(huì)詳細(xì)介紹,我們只需要通過(guò)反匯編簡(jiǎn)單看一下即可。
??????????????????圖片五:第一次寫(xiě)入shellCode
??????????????????圖片六:第二次寫(xiě)入shellCode
??????????????????圖片七:第三次寫(xiě)入shellCode
??????????????????圖片八:第四次寫(xiě)入shellCode
在第四次寫(xiě)入之后,又做了一些事情,如創(chuàng)建了事件(保證以下操作在多線程環(huán)境下安全),創(chuàng)建了一個(gè)全局句柄,如下圖所示:
??????????????????圖片九:事件及新句柄創(chuàng)建
??注意:第五次寫(xiě)入的是函數(shù)地址,圖片中的注釋是第一次分析時(shí)候注釋?zhuān)⒉皇荌AT,也不是修復(fù)重定位,只是為了方便shellCode調(diào)用而寫(xiě)入的地址,在目標(biāo)程序中shellCode會(huì)用到的函數(shù)地址,作為一個(gè)格外的附加項(xiàng)寫(xiě)入到了目標(biāo)程序,如下圖所示:
??????????????????圖片十:第五次寫(xiě)入shellCode
?? 創(chuàng)建遠(yuǎn)程線程及且把第五次寫(xiě)入的shellCode作為參數(shù)執(zhí)行:
??????????????????圖片十一:創(chuàng)建遠(yuǎn)程線程
?以上就是整個(gè)目標(biāo)程序注入的過(guò)程,發(fā)現(xiàn)并不復(fù)雜,這時(shí)候又要考慮,注入到目標(biāo)進(jìn)程shellCode,如何去分析這些代碼呢?
二、?shellCode調(diào)試方法:
?第一次用的是dump,dump下來(lái)的是丟失的、不是完整的代碼,思路很阻塞...... 后來(lái)找朋友請(qǐng)教了一些問(wèn)題,思考后大體有以下兩種辦法供參考:
??1、手動(dòng)構(gòu)建pe文件,修改shellCode或者寫(xiě)入到目標(biāo)進(jìn)程中shellCode,在虛擬內(nèi)存空間二進(jìn)制復(fù)制出來(lái)。二進(jìn)制復(fù)制的代碼拖入IDA中,我們需要手動(dòng)去找些函數(shù)名稱(chēng)(根據(jù)第五次寫(xiě)入的函數(shù)),這樣雖然能達(dá)到靜態(tài)分析的過(guò)程,但是相對(duì)比較麻煩。下面是在010中打開(kāi)的復(fù)制的shellCode,我們可以看到與第5次寫(xiě)入的函數(shù)完全一致,如下圖所示:
??????????????????圖片十二:010中查看數(shù)據(jù)
??2、雙進(jìn)程動(dòng)態(tài)調(diào)試,在目標(biāo)程序中分析觀察(動(dòng)態(tài))。簡(jiǎn)單點(diǎn)來(lái)說(shuō),被注入的進(jìn)程是你能夠附加而且可以調(diào)試的程序(有網(wǎng)絡(luò)響應(yīng))。就能動(dòng)態(tài)的觀察虛擬內(nèi)存的申請(qǐng)、寫(xiě)入的過(guò)程。能下內(nèi)存訪問(wèn)斷點(diǎn),能夠動(dòng)態(tài)的調(diào)試,而且是真實(shí)的應(yīng)用環(huán)境下進(jìn)行的,更為精準(zhǔn)。
?第三部分的內(nèi)容將采用這種方式進(jìn)行解析,分析代碼都干了什么事情?是怎樣捕獲這些網(wǎng)絡(luò)數(shù)據(jù)?下面我們一起來(lái)看。
三、?shellCode動(dòng)態(tài)分析:
?1、雙進(jìn)程調(diào)試,注入程序與被注入程序。當(dāng)注入程序(也就是圖一軟件),在目標(biāo)進(jìn)程中創(chuàng)建虛擬內(nèi)存空間后,EAX會(huì)返回創(chuàng)建成功的地址,我們要到目標(biāo)進(jìn)程中找到地址,注意是目標(biāo)進(jìn)程中!
?2、一般會(huì)遇到這種問(wèn)題:在目標(biāo)進(jìn)程中Ctrl+G查找地址的時(shí)候會(huì)找不到注入程序申請(qǐng)的虛擬內(nèi)存?明明申請(qǐng)都成功了為何還找不到?不慌!,我們?cè)贠D中Alt+M,然后拉到最下面(一般都在最下面),就會(huì)發(fā)現(xiàn)申請(qǐng)的虛擬內(nèi)存空間。
?3、當(dāng)注入的程序調(diào)用WriteProcessMemory,5次寫(xiě)入代碼的時(shí)候,我們就可以在目標(biāo)程序的數(shù)據(jù)窗口跟隨,動(dòng)態(tài)的觀察寫(xiě)入的數(shù)據(jù),直到5次寫(xiě)入完成。
?4、在創(chuàng)建遠(yuǎn)程線程之前,這時(shí)候目標(biāo)程序中的虛擬內(nèi)存應(yīng)該是有數(shù)據(jù)的,因?yàn)閷?xiě)入已經(jīng)完成。不要反匯編然后在申請(qǐng)的虛擬內(nèi)存中F2,好像也沒(méi)辦法F2下斷點(diǎn)。保險(xiǎn)起見(jiàn)直接下內(nèi)存訪問(wèn)斷點(diǎn)即可,然后注入程序創(chuàng)建遠(yuǎn)程線程成功,我們就可以讓目標(biāo)程序跑起來(lái),直接會(huì)在申請(qǐng)的虛擬內(nèi)存中斷下來(lái),剩下的就好辦了。
?我們開(kāi)始動(dòng)態(tài)調(diào)試shellCode,這段代碼先干了些什么?如下圖所示:
??????????????????圖片十三:獲取send、recv函數(shù)地址
?這段代碼先來(lái)了個(gè)獲取send、recv的函數(shù)地址,竟然這樣我們科普一下這兩個(gè)函數(shù),為了讓大家更容易理解,下面寫(xiě)了一段簡(jiǎn)單的網(wǎng)絡(luò)編程,來(lái)看以如何進(jìn)行網(wǎng)絡(luò)通訊。
?先來(lái)看函數(shù)原型,send與recv兩個(gè)函數(shù),分別是發(fā)送與響應(yīng),函數(shù)原型如下:
int WSAAPI recv(
_In_ SOCKET s,
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
_In_ int len,
_In_ int flags
);
int WSAAPI send(
_In_ SOCKET s,
_In_reads_bytes_(len) const char FAR * buf,
_In_ int len,
_In_ int flags
);
參數(shù)基本相同,第二個(gè)參數(shù)是指向char* 類(lèi)型的緩沖區(qū),這第三個(gè)參數(shù)是緩沖區(qū)大小,這兩個(gè)很關(guān)鍵。
服務(wù)器端:
#include "pch.h"
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "WS2_32.lib")
using namespace std;
/*
Socket網(wǎng)絡(luò)編程服務(wù)器端
*/
// 用于接受客戶端發(fā)來(lái)的消息 強(qiáng)轉(zhuǎn)后查看是否數(shù)據(jù)一致(精準(zhǔn))
typedef struct _Message
{
int Code;
char Number;
}Message, *pMessage;
int main()
{
cout << "服務(wù)端:" << endl;
WSADATA str_Data = { 0, };
int SockAddSize = sizeof(sockaddr_in);
int nResult = 0;
// 1. 初始化
nResult = WSAStartup(MAKEWORD(2, 2), &str_Data);
if (nResult == SOCKET_ERROR)
{
cout << "WSAStartup() ErrorCode = " << GetLastError() << endl;
system("pause");
return -1;
}
// 2. 創(chuàng)建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 3. 初始化Ip及端口信息
sockaddr_in str_Addrs = { 0, };
str_Addrs.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
str_Addrs.sin_family = AF_INET;
str_Addrs.sin_port = htons(8888);
// 4. 綁定socket
nResult = bind(sock, (sockaddr*)&str_Addrs, SockAddSize);
if (SOCKET_ERROR == nResult)
{
closesocket(sock);
WSACleanup();
cout << "bind() failuer ErrorCode = " << GetLastError() << endl;
return -1;
}
// 5. 監(jiān)聽(tīng)(失敗幾率與種500w有一拼,所以不做判斷)
try
{
listen(sock, SOMAXCONN);
}
catch (const std::exception&)
{
return -1;
}
sockaddr_in str_Client = { 0, };
// 6. 連接響應(yīng)(如果不設(shè)置異步 會(huì)阻塞等待 tcp),知道有客戶端去連接
SOCKET ClientSock = accept(sock, (sockaddr *)&str_Client, &SockAddSize);
if (ClientSock == INVALID_SOCKET)
{
closesocket(sock);
WSACleanup();
cout << "bind() failuer ErrorCode = " << GetLastError() << endl;
}
char nBuf[] = "消息已收到!";
int BufSize = sizeof(nBuf);
Message str_Msg = {0,};
// 7. 等待連接(這是一個(gè)死循環(huán))
// 如果有客戶端連接成功,發(fā)送一條消息看是否成功
if (SOCKET_ERROR == recv(ClientSock, (char*)&str_Msg, sizeof(Message), 0))
cout << "recvError Code = " << GetLastError() << endl;
cout << "客戶端發(fā)來(lái)消息: Code = " << str_Msg.Code << endl;
cout << "客戶端發(fā)來(lái)消息: Code = " << str_Msg.Number << endl;
// 回復(fù)客戶端一條消息
send(ClientSock, nBuf, BufSize, 0);
system("pause");
return 0;
}
客戶端:
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#pragma comment (lib, "WS2_32.lib")
using namespace std;
/*
Socket客戶端
*/
// 使用結(jié)構(gòu)體 更直觀表示通過(guò)send可以傳送大量的數(shù)據(jù)
typedef struct _Message
{
int Code;
char Number;
}Message, *pMessage;
int main()
{
cout << "客戶端:" << endl;
WSADATA str_Data = { 0, };
int nRet = 0;
// 1. 初始化
nRet = WSAStartup(MAKEWORD(2, 2), &str_Data);
if (SOCKET_ERROR == nRet)
{
cout << "WSAStartup() ErrorCode = " << GetLastError() << endl;
return -1;
}
// 2. Socket初始化
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in str_sockAdd = { 0, };
str_sockAdd.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
str_sockAdd.sin_family = AF_INET;
str_sockAdd.sin_port = htons(8888);
int socketSize = sizeof(sockaddr_in);
nRet = connect(sock, (sockaddr *)&str_sockAdd, socketSize);
if (SOCKET_ERROR == nRet)
{
cout << "connect failuer ErrorCode = " << GetLastError() << endl;
closesocket(sock);
WSACleanup();
return -1;
}
Message str_Msg = { 0, };
str_Msg.Code = 1;
str_Msg.Number = 'a';
// 成功消息到服務(wù)器端
send(sock, (char*)&str_Msg, sizeof(Message), 0);
char nBuf[20] = {0,};
// 響應(yīng)服務(wù)器發(fā)來(lái)的消息
recv(sock, nBuf, sizeof(nBuf), 0);
cout << "服務(wù)器端發(fā)來(lái)消息:" << nBuf << endl;
system("pause");
return 0;
}
?如果對(duì)網(wǎng)絡(luò)編程不熟悉,請(qǐng)把上面代碼學(xué)習(xí)一下,因?yàn)橄旅媸菍?duì)這兩個(gè)函數(shù)的inlinehook,所以掌握函數(shù)使用與實(shí)現(xiàn)很重要。
?如果對(duì)hook不熟悉,請(qǐng)看以前寫(xiě)的博客https://blog.51cto.com/13352079/2342776
上面我們分析了shellCode第一段代碼,獲取了recv與send函數(shù),下面接著上圖:
??????????????????圖片十三:讀取函數(shù)前5個(gè)字節(jié)
??????????????????圖片十四:inlinehook的offset計(jì)算
??????????????????圖片十五:替換原函數(shù)前5個(gè)字節(jié)
簡(jiǎn)單的打個(gè)比方:
?先讀取原函數(shù)send的前5個(gè)字節(jié),然后計(jì)算偏移: 中轉(zhuǎn)地址 - 原函數(shù)地址 - 5。為什么-5?如圖十五所示,原函數(shù)前5個(gè)字節(jié)hook后變?yōu)閖mp,運(yùn)行后被響應(yīng)然后跳轉(zhuǎn),如果你不-5,那不是又到了jmp,應(yīng)該jmp執(zhí)行之后,該執(zhí)行jmp下一條指令,所以-5。
?如果還不太清楚,我們來(lái)做一個(gè)對(duì)比 hook前與hook后發(fā)生了哪些變化,如下圖所示:
??????????????????圖片十六:hook之前的函數(shù)
??????????????????圖片十七:hook之后的函數(shù)
?所以破壞了原函數(shù)前5個(gè)字節(jié),一開(kāi)始先讀取是為了保存前5個(gè)字節(jié)的內(nèi)容,執(zhí)行JMP以后,跳轉(zhuǎn)到JMP下一條指令之前(SUB ESP,0X20之前)還是會(huì)執(zhí)行保存的5個(gè)字節(jié)機(jī)器碼,在跳轉(zhuǎn)到SUB ESP,0X20繼續(xù)執(zhí)行原函數(shù)。
inlinehook的recv函數(shù)干些什么事??
??????????????????圖片十八:hook recv執(zhí)行過(guò)程
?注意!如上圖所示,上述圖片中缺少少一個(gè)步驟,上面圖關(guān)聯(lián)到一起只是為了讓大家好理解,但是缺少了執(zhí)行原函數(shù)棧頂?shù)牟僮?,其?shí)CALL DWORD PTR DS:[ESI + 0XA90C]是跳轉(zhuǎn)到自己的shellCode中,然后執(zhí)行原函數(shù)的前5個(gè)字節(jié),如圖十二所示,到底CALL的是什么內(nèi)容?如下圖所示:
??????????????????圖片十九:執(zhí)行原函數(shù)棧頂
?如上圖所示,CALL過(guò)來(lái)之后,執(zhí)行機(jī)器指令8BFF558BEC(原函數(shù)的前5個(gè)字節(jié)),后面則是JMP ws2_32.74BF5FF5,其實(shí)就是 :中轉(zhuǎn)地址 - (send或者recv函數(shù)地址) - 5,上面介紹計(jì)算的偏移的作用就體現(xiàn)出來(lái)了,正好跳轉(zhuǎn)到原函數(shù)的JMP下一條指令。
inlinehook的send函數(shù)干些什么事??
??????????????????圖片二十:截獲send函數(shù)
??????????????????圖片二十一:hook send執(zhí)行流程1
?根據(jù)截獲跳轉(zhuǎn)到BaseAddress + 0x400的地方,Getpc獲取了當(dāng)前的地址,注意GetPC這種方式,如E8 00000000是敏感操作,有時(shí)候這樣使用當(dāng)前地址以下的匯編指令將被截?cái)?/strong>,繼續(xù)看:
??????????????????圖片二十二:hook send執(zhí)行流程2
?利用CreateFile在\.\Pipe\下面帶開(kāi)了文件句柄(圖三中的文件路徑),格式化輸出的是什么?
??????????????????圖片二十三:wvsprintfA函數(shù)
?格式化輸出,我們看到了一些關(guān)鍵的數(shù)據(jù),如上圖中PID,TID等等,為了傳送給網(wǎng)絡(luò)監(jiān)控工具顯示數(shù)據(jù)而準(zhǔn)備。
??????????????????圖片二十四:截獲的send消息寫(xiě)入文件句柄
?其實(shí)是ASCII截獲的數(shù)據(jù)則是第二個(gè)參數(shù),也就是緩沖區(qū)中的內(nèi)容加上PID一些附加信息數(shù)據(jù),寫(xiě)入大小是第三個(gè)參數(shù)加上PID等附加大小。注入程序去讀取文件句柄內(nèi)容,把捕獲的消息數(shù)據(jù)通過(guò)ListView控件(MFC)顯示到界面中。
?簡(jiǎn)單來(lái)說(shuō),就干了這么一件事,利用inlinehook技術(shù),hook send 和 recv兩個(gè)函數(shù),截獲第二個(gè)參數(shù)中的緩沖區(qū),顯示到三環(huán)。所以使用windows SDK網(wǎng)絡(luò)編程,或者說(shuō)使用這兩個(gè)函數(shù),你發(fā)送與響應(yīng)的消息會(huì)被截獲。
?到此你應(yīng)該知道軟件實(shí)現(xiàn)原理及過(guò)程,可以自己寫(xiě)一個(gè)更適用的網(wǎng)絡(luò)軟件,還可以做過(guò)濾,對(duì)一些敏感的數(shù)據(jù)操作,從而實(shí)現(xiàn)三環(huán)的網(wǎng)絡(luò)監(jiān)控功能、過(guò)濾功能等。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。