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网络堆栈发送的网络数据包,而无需编写内核模式代码。
主要有以下功能:
- 捕获网络数据包
- 筛选/丢弃网络数据包
- 嗅探网络数据包
- 注入网络数据包
- 修改网络数据包
主要特点:可抓包,修改包
注意:需要管理员权限!!!
网络博客教程地址:
https://www.freesion.com/article/9592776210/
https://blog.csdn.net/qq_33053671/article/details/76559784
官方示例
-
netdump.exe
一种基于WinDivert滤波器语言的简单数据包嗅探器。此程序采用在命令行中指定的筛选器,并打印与筛选器匹配的任何数据包的信息。此示例在“数据包嗅探”模式中使用WinDivert,类似于winpcap,但是与winpcap不同的是,winDivert可以看到本地回送数据包。 -
webfilter.exe
一个简单的url黑名单过滤器。此程序监视出站http通信量。如果找到与黑名单相匹配的url请求,它将劫持tcp连接,并向浏览器发送一个简单的页面。黑名单在command-line指定 -
netfilter.exe
基于winDivert过滤器语言的简单防火墙。此程序采用在命令行中指定的筛选器,并阻止与筛选器匹配的任何数据包。它通过发送tcp重置、icmp消息的udp,以及它简单丢弃的所有其它通信来阻止tcp -
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";
}