不以物喜,不以己悲

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";
}
posted @ 2022-03-31 21:09  这种人  阅读(3475)  评论(0编辑  收藏  举报