91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

某網絡監視器完整逆向

發布時間:2020-07-12 07:01:21 來源:網絡 閱讀:2598 作者:長路慢 欄目:編程語言

?引子:
? 早些時候想去研究Windows Filter Platform (WFP),參考資料少且不齊全。貼吧、論壇搜集一些關于網絡過濾、網絡監聽的工具。開始琢磨別人是怎樣寫,怎樣實現的。然而沒有去研究驅動層(很多原理性的東西需要時間),自己寫用戶層前一直琢磨,三環如何去實現這些網絡監聽?用什么API可以實現對數據包的捕獲呢?怎樣把這些數據進行處理?
? 當我看到其中的項目的時候,單純的.exe文件,運行后也沒有釋放dll之類的動態資源,腦海中出現一個念頭shellCode(這里就先叫shellCode了,其實準確說是機器碼)。這個程序是好多年前的,比較單一,注入任意進程,捕獲網絡響應數據,兼容性也還不錯,用360瀏覽器做測試,windows7~windwos10網絡響應捕獲正常。
?這是給大家提供一些逆向的思路,并不是教程系列,有一定逆向基礎才可以(對匯編、網絡編程、OD等工具了解)。當遇到類似的程序或者問題,對他們的實現原理做到心中有度。

  • 如下圖所示:
    某網絡監視器完整逆向
    ??????????????????圖片一:網絡監控exe

?逆向分析目錄:

1、注入代碼分析 2、shellCode調試方法 3、shellCode動態分析
--------★ ★ ★------- -----------------★ ★ ★------------------- -----------★ ★ ★ ★-----------

?草稿示意圖:
某網絡監視器完整逆向
??????????????????圖片二:程序流程草圖

一、?注入代碼分析:
?? 用IDA先簡略的瀏覽一下匯編指令,發現反匯編代碼不算多,了解了基本的程序結構,拖到OD開始動態調試。
?? 如圖二中第一步所示,獲取被注入的數據,需要獲取選中的目標進程Id等,并且OpenProcess打開目標進程,獲取句柄才可以完成注入,如下圖所示(圖中關鍵代碼已給出解釋):
某網絡監視器完整逆向
某網絡監視器完整逆向
??????????????????圖片三:獲取目標進程信息及獲取句柄
?? 目標進程申請虛擬內存,如下圖所示:
某網絡監視器完整逆向
??????????????????圖片四:申請虛擬內存空間
?? 目標進程虛擬內存申請之后,寫入shellCode,且5次寫入目標程序申請的虛擬內存空間,這個地方我們無需關系寫入shellCode的內容及作用,后面會詳細介紹,我們只需要通過反匯編簡單看一下即可。
某網絡監視器完整逆向
??????????????????圖片五:第一次寫入shellCode
某網絡監視器完整逆向
??????????????????圖片六:第二次寫入shellCode
某網絡監視器完整逆向
??????????????????圖片七:第三次寫入shellCode
某網絡監視器完整逆向
??????????????????圖片八:第四次寫入shellCode
在第四次寫入之后,又做了一些事情,如創建了事件(保證以下操作在多線程環境下安全),創建了一個全局句柄,如下圖所示:
某網絡監視器完整逆向
??????????????????圖片九:事件及新句柄創建
??注意第五次寫入的是函數地址圖片中的注釋是第一次分析時候注釋,并不是IAT,也不是修復重定位,只是為了方便shellCode調用而寫入的地址,在目標程序中shellCode會用到的函數地址,作為一個格外的附加項寫入到了目標程序,如下圖所示:
某網絡監視器完整逆向

??????????????????圖片十:第五次寫入shellCode
?? 創建遠程線程及且把第五次寫入的shellCode作為參數執行:
某網絡監視器完整逆向
??????????????????圖片十一:創建遠程線程
?以上就是整個目標程序注入的過程,發現并不復雜,這時候又要考慮,注入到目標進程shellCode,如何去分析這些代碼呢?

二、?shellCode調試方法:
?第一次用的是dump,dump下來的是丟失的、不是完整的代碼,思路很阻塞...... 后來找朋友請教了一些問題,思考后大體有以下兩種辦法供參考:
??1、手動構建pe文件,修改shellCode或者寫入到目標進程中shellCode,在虛擬內存空間二進制復制出來。二進制復制的代碼拖入IDA中,我們需要手動去找些函數名稱(根據第五次寫入的函數),這樣雖然能達到靜態分析的過程,但是相對比較麻煩。下面是在010中打開的復制的shellCode,我們可以看到與第5次寫入的函數完全一致,如下圖所示:
某網絡監視器完整逆向
??????????????????圖片十二:010中查看數據
??2、雙進程動態調試,在目標程序中分析觀察(動態)。簡單點來說,被注入的進程是你能夠附加而且可以調試的程序(有網絡響應)。就能動態的觀察虛擬內存的申請、寫入的過程。能下內存訪問斷點,能夠動態的調試,而且是真實的應用環境下進行的,更為精準。
?第三部分的內容將采用這種方式進行解析,分析代碼都干了什么事情?是怎樣捕獲這些網絡數據?下面我們一起來看。
三、?shellCode動態分析:
?1、雙進程調試,注入程序與被注入程序。當注入程序(也就是圖一軟件),在目標進程中創建虛擬內存空間后,EAX會返回創建成功的地址我們要到目標進程中找到地址,注意是目標進程中!
?2、一般會遇到這種問題:在目標進程中Ctrl+G查找地址的時候會找不到注入程序申請的虛擬內存?明明申請都成功了為何還找不到?不慌!,我們在OD中Alt+M,然后拉到最下面(一般都在最下面),就會發現申請的虛擬內存空間。
?3、當注入的程序調用WriteProcessMemory,5次寫入代碼的時候,我們就可以在目標程序的數據窗口跟隨,動態的觀察寫入的數據,直到5次寫入完成。
?4、在創建遠程線程之前,這時候目標程序中的虛擬內存應該是有數據的,因為寫入已經完成。不要反匯編然后在申請的虛擬內存中F2,好像也沒辦法F2下斷點。保險起見直接下內存訪問斷點即可,然后注入程序創建遠程線程成功,我們就可以讓目標程序跑起來,直接會在申請的虛擬內存中斷下來,剩下的就好辦了。

?我們開始動態調試shellCode,這段代碼先干了些什么?如下圖所示:
某網絡監視器完整逆向
??????????????????圖片十三:獲取send、recv函數地址
?這段代碼先來了個獲取send、recv的函數地址,竟然這樣我們科普一下這兩個函數,為了讓大家更容易理解,下面寫了一段簡單的網絡編程,來看以如何進行網絡通訊。
?先來看函數原型,send與recv兩個函數,分別是發送與響應,函數原型如下:

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
    );

參數基本相同,第二個參數是指向char* 類型的緩沖區,這第三個參數是緩沖區大小,這兩個很關鍵。

服務器端:

#include "pch.h"
#include <WinSock2.h>
#include <iostream>
#pragma comment(lib, "WS2_32.lib")

using namespace std;

/*
    Socket網絡編程服務器端
*/

// 用于接受客戶端發來的消息 強轉后查看是否數據一致(精準)
typedef struct _Message
{
    int Code;

    char Number;
}Message, *pMessage;

int main()
{
    cout << "服務端:" << 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. 創建套接字
    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. 監聽(失敗幾率與種500w有一拼,所以不做判斷)
    try
    {
        listen(sock, SOMAXCONN);
    }
    catch (const std::exception&)
    {
        return -1;
    }

    sockaddr_in str_Client = { 0, };

    // 6. 連接響應(如果不設置異步 會阻塞等待 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. 等待連接(這是一個死循環)

    // 如果有客戶端連接成功,發送一條消息看是否成功
    if (SOCKET_ERROR == recv(ClientSock, (char*)&str_Msg, sizeof(Message), 0))
        cout << "recvError Code =  " << GetLastError() << endl;

    cout << "客戶端發來消息:  Code = " << str_Msg.Code << endl;

    cout << "客戶端發來消息:  Code = " << str_Msg.Number << endl;

    // 回復客戶端一條消息
    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客戶端
*/

// 使用結構體 更直觀表示通過send可以傳送大量的數據
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';

    // 成功消息到服務器端
    send(sock, (char*)&str_Msg, sizeof(Message), 0);

    char nBuf[20] = {0,};
    // 響應服務器發來的消息
    recv(sock, nBuf, sizeof(nBuf), 0);

    cout << "服務器端發來消息:" << nBuf << endl;

    system("pause");

    return 0;
}

?如果對網絡編程不熟悉,請把上面代碼學習一下,因為下面是對這兩個函數的inlinehook,所以掌握函數使用與實現很重要。
?如果對hook不熟悉,請看以前寫的博客https://blog.51cto.com/13352079/2342776

上面我們分析了shellCode第一段代碼,獲取了recv與send函數,下面接著上圖:

某網絡監視器完整逆向
??????????????????圖片十三:讀取函數前5個字節
某網絡監視器完整逆向
??????????????????圖片十四:inlinehook的offset計算
某網絡監視器完整逆向
??????????????????圖片十五:替換原函數前5個字節
簡單的打個比方:
?先讀取原函數send的前5個字節,然后計算偏移: 中轉地址 - 原函數地址 - 5。為什么-5?如圖十五所示,原函數前5個字節hook后變為jmp,運行后被響應然后跳轉,如果你不-5,那不是又到了jmp,應該jmp執行之后,該執行jmp下一條指令,所以-5。
?如果還不太清楚,我們來做一個對比 hook前與hook后發生了哪些變化,如下圖所示:
某網絡監視器完整逆向
??????????????????圖片十六:hook之前的函數
某網絡監視器完整逆向
??????????????????圖片十七:hook之后的函數
?所以破壞了原函數前5個字節,一開始先讀取是為了保存前5個字節的內容,執行JMP以后,跳轉到JMP下一條指令之前(SUB ESP,0X20之前)還是會執行保存的5個字節機器碼,在跳轉到SUB ESP,0X20繼續執行原函數。
inlinehook的recv函數干些什么事??
某網絡監視器完整逆向
??????????????????圖片十八:hook recv執行過程
?注意!如上圖所示,上述圖片中缺少少一個步驟,上面圖關聯到一起只是為了讓大家好理解,但是缺少了執行原函數棧頂的操作,其實CALL DWORD PTR DS:[ESI + 0XA90C]是跳轉到自己的shellCode中,然后執行原函數的前5個字節,如圖十二所示,到底CALL的是什么內容?如下圖所示:
某網絡監視器完整逆向
??????????????????圖片十九:執行原函數棧頂
?如上圖所示,CALL過來之后,執行機器指令8BFF558BEC(原函數的前5個字節),后面則是JMP ws2_32.74BF5FF5,其實就是 :中轉地址 - (send或者recv函數地址) - 5,上面介紹計算的偏移的作用就體現出來了,正好跳轉到原函數的JMP下一條指令。
inlinehook的send函數干些什么事??
某網絡監視器完整逆向
??????????????????圖片二十:截獲send函數
某網絡監視器完整逆向
??????????????????圖片二十一:hook send執行流程1
?根據截獲跳轉到BaseAddress + 0x400的地方,Getpc獲取了當前的地址,注意GetPC這種方式,如E8 00000000是敏感操作,有時候這樣使用當前地址以下的匯編指令將被截斷,繼續看:
某網絡監視器完整逆向
??????????????????圖片二十二:hook send執行流程2
?利用CreateFile在\.\Pipe\下面帶開了文件句柄(圖三中的文件路徑),格式化輸出的是什么?
某網絡監視器完整逆向
??????????????????圖片二十三:wvsprintfA函數
?格式化輸出,我們看到了一些關鍵的數據,如上圖中PID,TID等等,為了傳送給網絡監控工具顯示數據而準備。
某網絡監視器完整逆向
??????????????????圖片二十四:截獲的send消息寫入文件句柄
?其實是ASCII截獲的數據則是第二個參數,也就是緩沖區中的內容加上PID一些附加信息數據,寫入大小是第三個參數加上PID等附加大小。注入程序去讀取文件句柄內容,把捕獲的消息數據通過ListView控件(MFC)顯示到界面中。
?簡單來說,就干了這么一件事,利用inlinehook技術,hook send 和 recv兩個函數,截獲第二個參數中的緩沖區,顯示到三環。所以使用windows SDK網絡編程,或者說使用這兩個函數,你發送與響應的消息會被截獲。
?到此你應該知道軟件實現原理及過程,可以自己寫一個更適用的網絡軟件,還可以做過濾,對一些敏感的數據操作,從而實現三環的網絡監控功能、過濾功能等。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

额尔古纳市| 红原县| 长泰县| 清远市| 迁西县| 启东市| 盐山县| 稷山县| 旬阳县| 盐亭县| 黑龙江省| 尚义县| 京山县| 大连市| 沙田区| 德州市| 客服| 徐州市| 安乡县| 金川县| 苍溪县| 凤山市| 方正县| 乌拉特后旗| 随州市| 灌阳县| 桓台县| 长乐市| 宜城市| 呼玛县| 永安市| 迁西县| 石嘴山市| 余庆县| 嵊州市| 交城县| 乌苏市| 车险| 鲁山县| 海原县| 松溪县|