WinDivert學習

簡介

官方網址

官網:https://reqrypt.org/windivert.html

官方文檔:https://reqrypt.org/windivert-doc.html

github:https://github.com/basil00/Divert

採用的是LGPL開源協議,可免費用於商用

介紹

WinDivert是一種用戶模式的數據包捕獲和轉移包,可用於windows vista、windows 2008、windows 7、windows 8 和 windows 10。WinDivert允許用戶模式應用程序捕獲、修改、丟棄從windows網絡堆棧發送的網絡數據包,而無需編寫內核模式代碼。

主要有以下功能:

  1. 捕獲網絡數據包
  2. 篩選/丟棄網絡數據包
  3. 嗅探網絡數據包
  4. 注入網絡數據包
  5. 修改網絡數據包
    主要特點:可抓包,修改包

注意:需要管理員權限!!!

網絡博客教程地址:

https://www.freesion.com/article/9592776210/

https://blog.csdn.net/qq_33053671/article/details/76559784

官方示例

  1. netdump.exe
    一種基於WinDivert濾波器語言的簡單數據包嗅探器。此程序採用在命令行中指定的篩選器,並打印與篩選器匹配的任何數據包的信息。此示例在“數據包嗅探”模式中使用WinDivert,類似於winpcap,但是與winpcap不同的是,winDivert可以看到本地回送數據包。

  2. webfilter.exe
    一個簡單的url黑名單過濾器。此程序監視出站http通信量。如果找到與黑名單相匹配的url請求,它將劫持tcp連接,並向瀏覽器發送一個簡單的頁面。黑名單在command-line指定

  3. netfilter.exe
    基於winDivert過濾器語言的簡單防火牆。此程序採用在命令行中指定的篩選器,並阻止與篩選器匹配的任何數據包。它通過發送tcp重置、icmp消息的udp,以及它簡單丟棄的所有其它通信來阻止tcp

  4. passthru.exe
    一個簡單的程序,它簡單的re-injects每個數據包捕獲。此示例是多線程,其中多個線程正在處理來自單個句柄的數據包。此示例適用於性能測試。

基本API

WinDivertOpen 打開

/*
 * Open a WinDivert handle.
 */
WINDIVERTEXPORT HANDLE WinDivertOpen(
    __in        const char *filter,
    __in        WINDIVERT_LAYER layer,
    __in        INT16 priority,
    __in        UINT64 flags);

打開一個windivert對象,返回一個對象指針。打開的過程中,需要指定過濾規則,過濾層,過濾器的優先級,以及windivert對象的工作模式。
WINDIVERT_LAYER:

Layer 描述
WINDIVERT_LAYER_NETWORK=0 網絡層
WINDIVERT_LAYER_NETWORK_FORWARD 轉發層

priority:過濾規則的優先級。值越小,優先級越大。-1000的優先級最高,1000優先級最低。對於一個數據包,如果它同時匹配多個windivert對象的規則,那麼,它依次被這些對象安裝優先級從高到低的次序處理。

flags:該參數指明windivert對象到底是用於監聽、丟包、還是修改包模式。

Flag 描述
WINDIVERT_FLAG_SNIFF windivert進入監聽模式,只是被動監聽,其功能等同於Winpcap
WINDIVERT_FLAG_DROP windivert單純地把滿足過濾條件的包丟棄,此模式下不能讀取包內容

WinDivertHelperChheckFilter 檢測過濾條件是否合法

/*
 * Compile the given filter string.
 */
WINDIVERTEXPORT BOOL WinDivertHelperCompileFilter(
    __in        const char *filter,
    __in        WINDIVERT_LAYER layer,
    __out_opt   char *object,
    __in        UINT objLen,
    __out_opt   const char **errorStr,
    __out_opt   UINT *errorPos);

WinDivertRecv 接收原包

/*
 * Receive (read) a packet from a WinDivert handle.
 */
WINDIVERTEXPORT BOOL WinDivertRecv(
    __in        HANDLE handle,
    __out_opt   VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pRecvLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr);

接收特定WinDivert對象的捕獲的包的函數。
pPacket:用於存儲包的buffer,緩衝區,這個是用戶提供

packetLen:緩衝區的大小,如果包的實際長度大於此值,則截取packetLen個字節到pPacket

pAddr:包性質說明結構。該結構體,指明目前抓取到的包的性質

typedef struct
{
    INT64  Timestamp;                   /* Packet's timestamp. */
    UINT32 Layer:8;                     /* Packet's layer. */
    UINT32 Event:8;                     /* Packet event. */
    UINT32 Sniffed:1;                   /* Packet was sniffed? */
    UINT32 Outbound:1;                  /* Packet is outound? */
    UINT32 Loopback:1;                  /* Packet is loopback? */
    UINT32 Impostor:1;                  /* Packet is impostor? */
    UINT32 IPv6:1;                      /* Packet is IPv6? */
    UINT32 IPChecksum:1;                /* Packet has valid IPv4 checksum? */
    UINT32 TCPChecksum:1;               /* Packet has valid TCP checksum? */
    UINT32 UDPChecksum:1;               /* Packet has valid UDP checksum? */
    UINT32 Reserved1:8;
    UINT32 Reserved2;
    union
    {
        WINDIVERT_DATA_NETWORK Network; /* Network layer data. */
        WINDIVERT_DATA_FLOW Flow;       /* Flow layer data. */
        WINDIVERT_DATA_SOCKET Socket;   /* Socket layer data. */
        WINDIVERT_DATA_REFLECT Reflect; /* Reflect layer data. */
        UINT8 Reserved3[64];
    };
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;

WinDivertHelperParsePacket 解析原包信息

/*
 * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet.
 */
WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   PWINDIVERT_IPHDR *ppIpHdr,
    __out_opt   PWINDIVERT_IPV6HDR *ppIpv6Hdr,
    __out_opt   UINT8 *pProtocol,
    __out_opt   PWINDIVERT_ICMPHDR *ppIcmpHdr,
    __out_opt   PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
    __out_opt   PWINDIVERT_TCPHDR *ppTcpHdr,
    __out_opt   PWINDIVERT_UDPHDR *ppUdpHdr,
    __out_opt   PVOID *ppData,
    __out_opt   UINT *pDataLen,
    __out_opt   PVOID *ppNext,
    __out_opt   UINT *pNextLen);

WinDivertHelperCalcChecksums 改變檢驗值

/*
 * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums.
 */
WINDIVERTEXPORT BOOL WinDivertHelperCalcChecksums(
    __inout     VOID *pPacket, 
    __in        UINT packetLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr,
    __in        UINT64 flags);

注意這個函數會直接在pPacket緩衝區中計算各個校驗值!!!

WinDivertSend 發送原包

/*
 * Send (write/inject) a packet to a WinDivert handle.
 */
WINDIVERTEXPORT BOOL WinDivertSend(
    __in        HANDLE handle,
    __in        const VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pSendLen,
    __in        const WINDIVERT_ADDRESS *pAddr);

pPacket待發包的內容
packetLen:pPacket的總長度

pAddr:待發包的性質

sendLen:實際發送

注意:發包之前,一定要讓包有正確的校驗值!!!

WinDivertHelperFormatIPv4Address

ip地址轉換api,點分十進制轉INT

::WinDivertHelperFormatIPv4Address(::WinDivertHelperNtohl(iphdr->SrcAddr), srcAddress, 1024);

過濾語言

WinDivertOpen函數接受包含篩選表達式的字符串。只有與篩選器表達式匹配的數據包纔會被轉移。任何其它數據包都可以按正常狀態繼續。

使用最多的參數介紹:

  • inbound / outbound:表示處理收包和發包
  • udp / tcp:篩選不同的傳輸層協議
  • ip.*:ip地址篩選
  • udp.* / tcp.* :針對某一傳輸層進行更具體的篩選。

應用示例一:限定指定端口的接收速度並打印數據

#include <iostream>
#include<Windows.h>
#include "windivert.h"
#pragma comment(lib, "WinDivert.lib")
using namespace std;

int main()
{
    HANDLE handle = ::WinDivertOpen("tcp.DstPort == 3002 || tcp.SrcPort == 3002",  //過濾規則
        WINDIVERT_LAYER_NETWORK,  //過濾層
        0,  //優先級
        0);
    if (handle == INVALID_HANDLE_VALUE) // 開啓過濾對象
    {
        DWORD res = ::GetLastError();
        return 0;
    }
    UINT packetLen = WINDIVERT_MTU_MAX;
    char* packet = new char[WINDIVERT_MTU_MAX];
    UINT recvSize = 0;
    UINT sendSize = 0;
    WINDIVERT_ADDRESS addr = { 0 };
    CHAR outbuff[1024] = { 0 };
    while (true)
    {
        ZeroMemory(packet, packetLen);
        if (!::WinDivertRecv(handle,  //windivert對象
            packet, // 緩存
            packetLen, //緩存長度 
            &recvSize, // 實際讀取的數據長度
            &addr)) // WINDIVERT_ADDRESS對象
        {
            DWORD res = ::GetLastError();

            cout << "WinDivertRecv error:" << res << endl;
            continue;
        }
        //cout << "接收數據長度:" << recvSize << endl;
        PWINDIVERT_IPHDR iphdr = nullptr;
        PWINDIVERT_TCPHDR tcphdr = nullptr;
        PWINDIVERT_UDPHDR udphdr = nullptr;
        // 網絡包頭部解析
        if (!::WinDivertHelperParsePacket(packet, recvSize, &iphdr, nullptr, nullptr, nullptr, nullptr, &tcphdr, &udphdr, nullptr, nullptr, nullptr, nullptr))
        {
            DWORD res = ::GetLastError();

            cout << "WinDivertHelperParsePacket error:" << res << endl;
            continue;
        }
        if (tcphdr != nullptr)
        {
            UINT dstPort = WinDivertHelperHtons(tcphdr->DstPort);
            UINT srcPort = ::WinDivertHelperHtons(tcphdr->SrcPort);
            UINT length = ::WinDivertHelperHtons(iphdr->Length);
            char srcAddress[1024] = { 0 };
            char dstAddress[1024] = { 0 };
            ::WinDivertHelperFormatIPv4Address(::WinDivertHelperNtohl(iphdr->SrcAddr), srcAddress, 1024);
            ::WinDivertHelperFormatIPv4Address(::WinDivertHelperNtohl(iphdr->DstAddr), dstAddress, 1024);
            ZeroMemory(outbuff, sizeof(outbuff));
            sprintf_s(outbuff, "id:%d 長度:%d 目標IP:%s 端口:%d,本地IP:%s 端口:%d ACK:%d SYN:%d Fin:%d Rst:%d Psh:%d Urg:%d UrgPtr:%d Window:%d Checksum:%d", 
                iphdr->Id, length, dstAddress, dstPort, srcAddress, srcPort, tcphdr->Ack, tcphdr->Syn, tcphdr->Fin,
                tcphdr->Rst, tcphdr->Psh, tcphdr->Urg, tcphdr->UrgPtr, tcphdr->Window, tcphdr->Checksum);
            cout << outbuff << endl;
            INT tcpHeadLen = sizeof(WINDIVERT_TCPHDR);
            INT ipHeadLen = sizeof(WINDIVERT_IPHDR);
            INT dataLen = recvSize - 16 - ipHeadLen - tcpHeadLen;
            if (dataLen > 0) //解析數據
            {
                char* data = packet + 16 + ipHeadLen + tcpHeadLen;
                cout << data << endl; //輸出數據內容
            }
            else
            {
                //無有效數據
            }
        }
        //計算校驗和
        if (!::WinDivertHelperCalcChecksums(packet, packetLen, &addr, 0))
        {
            DWORD res = ::GetLastError();
            cout << "WinDivertHelperCalcChecksums error:" << res << endl;
            continue;
        }
        ::Sleep(3); //可延遲接收處理
        // 把修改後的包發送出去
        if (!::WinDivertSend(handle, packet, packetLen, &sendSize, &addr))
        {
            DWORD res = ::GetLastError();
            cout << "WinDivertSend: " << res << endl;
        }

    }
    ::WinDivertShutdown(handle, WINDIVERT_SHUTDOWN_BOTH);
    ::WinDivertClose(handle);
    std::cout << "Hello World!\n";
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章