L o a d i n g . . .
主打一个C++
文章详情

WinDivert网络数据拦截测试(C++)

Posted on 2021-07-15 16:58:45 by 主打一个C++

WinDivert是一个用于Windows的网络数据包捕获和修改的库,它允许你在用户态拦截、检查、修改和重定向网络数据包。WinDivert可以用于实现网络监控、过滤和修改等功能。

官网:WinDivert

关键函数:

WINDIVERTEXPORT HANDLE WinDivertOpen(
    __in        const char *filter,
    __in        WINDIVERT_LAYER layer,
    __in        INT16 priority,
    __in        UINT64 flags);

//参数说明

打开一个windivert对象,返回一个对象指针。打开的过程中,需要制定 过滤规则,过滤层,过滤器的优先级,以及windivert对象的工作模式。

过滤规则的编写:参考https://reqrypt.org/windivert-doc.html 第7部分

过滤层参数说明:

LayerDescription
WINDIVERT_LAYER_NETWORK = 0The network layer. This is the default. 网络层
WINDIVERT_LAYER_NETWORK_FORWARDThe network layer (forwarded packets).转发层

优先级的说明:其实这里说明的就是指明过滤规则的优先级。值越小,优先级越大。 -1000的优先级最高,1000优先级最低。 对于一个数据包,如果它同时匹配多个Windivert对象的规则,那么,它依次被这些对象安装优先级从高到低的次序处理。

过滤FLAG :该参数指明Windivert对象到底是用于监听、丢包、还是修改包模式。

FlagDescription
WINDIVERT_FLAG_SNIFFWinDivert进入监听模式,只是被动监听,其功能等同于Winpcap
WINDIVERT_FLAG_DROPWinDivert单纯地把满足过滤条件的包丢弃,此模式下不能读取包内容
WINDIVERT_FLAG_DEBUGWinDivert把满足过滤的条件的包扣下来,等待被修改然后重新注入
WINDIVERTEXPORT BOOL WinDivertRecv(
    __in        HANDLE handle,
    __out_opt   VOID *pPacket,
    __in        UINT packetLen,
    __out_opt   UINT *pRecvLen,
    __out_opt   WINDIVERT_ADDRESS *pAddr);

接收特定WinDivert对象的捕获的包的函数。
handle: WinDivertOpen的返回值
pPacket : 用于存储包的buffer,缓冲区,这个是用户提供的。
packetLen: 缓冲区的大小;如果包的实际长度大于此值,则截取packetLen个字节到pPacket。
pAddr :包性质说明结构。该结构体,指明目前抓取到的包的性质。说明如下:

typedef struct
{
    INT64  Timestamp;
    UINT32 IfIdx;
    UINT32 SubIfIdx;
    UINT8  Direction:1;
    UINT8  Loopback:1;
    UINT8  Impostor:1;
    UINT8  PseudoIPChecksum:1;
    UINT8  PseudoTCPChecksum:1;
    UINT8  PseudoUDPChecksum:1;
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;

字段说明:
Timestamp: WinDivert捕获到该包的时间戳
IfIdx: 该包所在的网卡序号
SubIfIdx: 网卡子序号
Direction: 包的方向,主要有入包和出包之分。

含义
WINDIVERT_DIRECTION_OUTBOUNDwith value 0 for outbound packets.
WINDIVERT_DIRECTION_INBOUNDwith value 1 for inbound packets.

Loopback: 是否是回环包。Set to 1 for loopback packets, 0 otherwise
Impostor: 是否是已经修改的包。Set to 1 for impostor packets, 0 otherwise. 注意,如果是修改后的包,这个值会被值为1。这个值主要是为了防止出现同一个包先被WinDivert捕获到,然后WinDivertSend后,又被捕获到。PseudoIPChecksum: Set to 1 for packets with a pseudo IPv4 checksum, 0 otherwise.
PseudoTCPChecksum: Set to 1 for packets with a pseudo TCP checksum, 0 otherwise.
PseudoTCPChecksum: Set to 1 for packets with a pseudo UDP checksum, 0 otherwise.

recvlen:实际拷贝到pPacket缓冲区的字节数。可能为0.

  • WinDivertSend
 BOOL WinDivertSend(
    __in HANDLE handle,
    __in PVOID pPacket,
    __in UINT packetLen,
    __in PWINDIVERT_ADDRESS pAddr,
    __out_opt UINT *sendLen
);

handle: WinDivertOpen()返回值.
pPacket: 待发包的内容.
packetLen: pPacket的总长度.
pAddr: 待发包的性质 WINDIVERT_ADDRESS.
sendLen: The total number of bytes injected. Can be NULL if this information is not required.

注意:发包之前 ,一定要让包有正确的校验值。!!!

  • WinDivertClose
BOOL WinDivertClose(
    __in HANDLE handle
);

一些辅助API

主要是一些包的解析、校验和的计算api.

  • WinDivertHelperParsePacket 头部解析api
BOOL WinDivertHelperParsePacket(
   __in PVOID pPacket,
   __in UINT packetLen,
   __out_opt PWINDIVERT_IPHDR *ppIpHdr,
   __out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
   __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
);
  • WinDivertHelperParseIPv4Address ip地址转换api ,点分十进制转INT。
 BOOL WinDivertHelperParseIPv4Address(
    __in const char *addrStr,
    __out_opt UINT32 *pAddr
);
  • WinDivertHelperCalcChecksums 计算校验值
 UINT WinDivertHelperCalcChecksums(
    __inout PVOID pPacket,
    __in UINT packetLen,
    __in_opt PWINDIVERT_ADDRESS pAddr,
    __in UINT64 flags
);

注意!这个函数会直接在pPacket缓冲区中计算各个校验值。返回值是表明计算了多少个校验值。

//示例代码:

#include <iostream>
#include<Windows.h>
#include "./WinDivert-2.2.2-A/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);
    HANDLE handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, 0, WINDIVERT_FLAG_DROP);
    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 && iphdr != 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!
";
}



*转载请注明出处:原文链接:https://cpp.vin/page/4.html

作者近期文章
  • 随手笔记
  • 主打一个C++   2025-01-11 20:02:01
  • 都2000000025年了。还有不能随意访问guthub的,仔细看。在国内其实是可以正常访问的,gfw并没屏蔽。这里给出其中一个简单直接的方法稳定访问。1. 随便百度一个”dn
提示
×
确定
数据库执行: 8次 总耗时: 0.01s
页面加载耗时: 



wechat +447752296473
wechat cpp-blog