簡介
官方網址
官網: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";
}