├── .gitattributes ├── .gitignore ├── App ├── App.csproj ├── Icmp │ ├── FastPinger.cs │ ├── PingPacket.cs │ └── RouteTracer.cs ├── Program.cs └── app.manifest ├── README.md ├── WindivertDotnet.Test ├── FilterTest.cs ├── HeaderSizeTest.cs ├── IPHeaderTest.cs ├── IcmpV4HeaderTest.cs ├── IcmpV6HeaderTest.cs ├── TcpHeaderTest.cs ├── UdpHeaderTest.cs ├── WinDivertAddressTest.cs ├── WinDivertBufferWriterTest.cs ├── WinDivertPacketExtensionsTest.cs ├── WinDivertPacketTest.cs ├── WindivertDotnet.Test.csproj └── WindivertRouterTest.cs ├── WindivertDotnet.sln ├── WindivertDotnet ├── ChecksumsFlag.cs ├── Event.cs ├── Filter.cs ├── FragmentFlag.cs ├── IFilter.cs ├── IPV4Header.cs ├── IPV6Header.cs ├── IPVersion.cs ├── IcmpV4Header.cs ├── IcmpV4MessageType.cs ├── IcmpV4UnreachableCode.cs ├── IcmpV6Header.cs ├── IcmpV6MessageType.cs ├── IcmpV6UnreachableCode.cs ├── IdSeqNum.cs ├── Runtime │ ├── InteropServices │ │ ├── IPHelpApiNative.cs │ │ ├── Kernel32Native.cs │ │ ├── MemoryNative.cs │ │ ├── WinDivertNative.Loader.cs │ │ └── WinDivertNative.cs │ └── Versioning │ │ └── SupportedOSPlatformAttribute.cs ├── SockAddress.cs ├── TcpFlag.cs ├── TcpHeader.cs ├── UdpHeader.cs ├── WinDivert.cs ├── WinDivertAddress.cs ├── WinDivertAddressFlag.cs ├── WinDivertDataFlow.cs ├── WinDivertDataNetwork.cs ├── WinDivertDataReflect.cs ├── WinDivertDataSocket.cs ├── WinDivertEvent.cs ├── WinDivertFlag.cs ├── WinDivertLayer.cs ├── WinDivertOperation.cs ├── WinDivertPacket.cs ├── WinDivertParam.cs ├── WinDivertParseResult.cs ├── WinDivertRecvOperation.cs ├── WinDivertRouter.cs ├── WinDivertSendOperation.cs ├── WinDivertShutdown.cs ├── WindivertBufferWriter.cs ├── WindivertDotnet.csproj └── v222 │ ├── x64 │ ├── WinDivert.dll │ └── WinDivert64.sys │ └── x86 │ ├── WinDivert.dll │ ├── WinDivert32.sys │ └── WinDivert64.sys ├── icon.png └── license /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /App/App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0-windows 6 | enable 7 | True 8 | win-x64 9 | app.manifest 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /App/Icmp/FastPinger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using WindivertDotnet; 10 | 11 | namespace App.Icmp 12 | { 13 | 14 | /// 15 | /// 快速ping工具 16 | /// 17 | static class FastPinger 18 | { 19 | private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum); 20 | 21 | /// 22 | /// Ping所有地址 23 | /// 24 | /// 开始地址 25 | /// IP数量 26 | /// 最后一个IP发出ping之后的等待回复时长 27 | /// 28 | /// 29 | public static Task PingAllAsync(IPAddress startAddr, int count, TimeSpan waitTime, CancellationToken cancellationToken = default) 30 | { 31 | var dstAddrs = CreateAddrs(startAddr, count); 32 | return PingAllAsync(dstAddrs, waitTime, cancellationToken); 33 | } 34 | 35 | /// 36 | /// 创建IP列表 37 | /// 38 | /// 39 | /// 40 | /// 41 | private static IEnumerable CreateAddrs(IPAddress startAddr, int count) 42 | { 43 | var bytes = startAddr.GetAddressBytes(); 44 | var start = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(bytes.Length - 4)); 45 | 46 | for (var i = 0; i < count; i++) 47 | { 48 | var value = (uint)(start + i); 49 | BinaryPrimitives.WriteUInt32BigEndian(bytes.AsSpan(bytes.Length - 4), value); 50 | yield return new IPAddress(bytes); 51 | } 52 | } 53 | 54 | /// 55 | /// Ping所有地址 56 | /// 57 | /// 目标地址 58 | /// 最后一个IP发出ping之后的等待回复时长 59 | /// 60 | /// 61 | public static async Task PingAllAsync(IEnumerable dstAddrs, TimeSpan waitTime, CancellationToken cancellationToken = default) 62 | { 63 | var filter = Filter.True 64 | .And(f => f.Network.Inbound) 65 | .And(f => f.ICmp.Type == IcmpV4MessageType.EchoReply || f.IcmpV6.Type == IcmpV6MessageType.EchoReply); 66 | 67 | using var divert = new WinDivert(filter, WinDivertLayer.Network); 68 | 69 | // 开始监听ping的回复 70 | using var waitTokenSource = new CancellationTokenSource(); 71 | using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken); 72 | var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token); 73 | var seqNums = await SendPingsAsync(divert, dstAddrs, cancellationToken); 74 | 75 | // 延时取消监听 76 | waitTokenSource.CancelAfter(waitTime); 77 | var results = await recvTask; 78 | 79 | return results 80 | .Where(item => seqNums.Contains(item.SeqNum)) 81 | .Select(item => item.SrcAddress) 82 | .Distinct() 83 | .ToArray(); 84 | } 85 | 86 | 87 | /// 88 | /// 接收回复信息 89 | /// 90 | /// 91 | /// 92 | /// 93 | private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken) 94 | { 95 | var results = new List(); 96 | using var packet = new WinDivertPacket(); 97 | using var addr = new WinDivertAddress(); 98 | 99 | while (cancellationToken.IsCancellationRequested == false) 100 | { 101 | try 102 | { 103 | await divert.RecvAsync(packet, addr, cancellationToken); 104 | 105 | if (TryParseReply(packet, out var srcAddrSeqNum)) 106 | { 107 | results.Add(srcAddrSeqNum); 108 | } 109 | 110 | await divert.SendAsync(packet, addr, cancellationToken); 111 | } 112 | catch (OperationCanceledException) 113 | { 114 | break; 115 | } 116 | } 117 | 118 | return results; 119 | } 120 | 121 | 122 | /// 123 | /// 解析出icmp回复信息 124 | /// 125 | /// 126 | /// 127 | /// 128 | private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum) 129 | { 130 | var result = packet.GetParseResult(); 131 | var srcAddr = result.IPV4Header != null 132 | ? result.IPV4Header->SrcAddr 133 | : result.IPV6Header->SrcAddr; 134 | 135 | if (result.IcmpV4Header != null) 136 | { 137 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV4Header->SequenceNumber); 138 | return true; 139 | } 140 | 141 | if (result.IcmpV6Header != null) 142 | { 143 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV6Header->SequenceNumber); 144 | return true; 145 | } 146 | 147 | srcAddrSeqNum = null; 148 | return false; 149 | } 150 | 151 | /// 152 | /// 发送ping请求 153 | /// 154 | /// 155 | /// 156 | /// 157 | /// 158 | private static async Task> SendPingsAsync(WinDivert divert, IEnumerable dstAddrs, CancellationToken cancellationToken) 159 | { 160 | var seqNums = new HashSet(); 161 | foreach (var address in dstAddrs) 162 | { 163 | // 使用router计算将进行通讯的本机地址 164 | var router = new WinDivertRouter(address); 165 | using var addr = router.CreateAddress(); 166 | 167 | using var packet = new PingPacket(router); 168 | packet.CalcChecksums(addr); 169 | await divert.SendAsync(packet, addr, cancellationToken); 170 | seqNums.Add(packet.SeqNum); 171 | } 172 | return seqNums; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /App/Icmp/PingPacket.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | using WindivertDotnet; 3 | 4 | namespace App.Icmp 5 | { 6 | /// 7 | /// ping包 8 | /// 9 | unsafe class PingPacket : WinDivertPacket 10 | { 11 | private static readonly IdSeqNum id = new(); 12 | private static readonly IdSeqNum seqNum = new(); 13 | private static readonly ushort v4PacketLength = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header) + 32); 14 | private static readonly ushort v6PacketLength = (ushort)(sizeof(IPV6Header) + sizeof(IcmpV6Header) + 32); 15 | 16 | public ushort Id { get; } = id.NextUInt16(); 17 | 18 | public ushort SeqNum { get; } = seqNum.NextUInt16(); 19 | 20 | /// 21 | /// ping包 22 | /// 23 | /// 24 | /// 25 | public PingPacket(WinDivertRouter router, byte ttl = 128) 26 | : base(v6PacketLength) 27 | { 28 | if (router.DstAddress.AddressFamily == AddressFamily.InterNetwork) 29 | { 30 | var ipHeader = new IPV4Header 31 | { 32 | TTL = ttl, 33 | Version = IPVersion.V4, 34 | DstAddr = router.DstAddress, 35 | SrcAddr = router.SrcAddress, 36 | Protocol = ProtocolType.Icmp, 37 | HdrLength = 5, 38 | Id = IdSeqNum.Shared.NextUInt16(), 39 | Length = v4PacketLength 40 | }; 41 | 42 | var icmpHeader = new IcmpV4Header 43 | { 44 | Type = IcmpV4MessageType.EchoRequest, 45 | Identifier = Id, 46 | SequenceNumber = SeqNum, 47 | }; 48 | 49 | var writer = GetWriter(); 50 | writer.Write(ipHeader); 51 | writer.Write(icmpHeader); 52 | writer.Advance(32); 53 | } 54 | else 55 | { 56 | var ipHeader = new IPV6Header 57 | { 58 | SrcAddr = router.SrcAddress, 59 | DstAddr = router.DstAddress, 60 | Length = v6PacketLength, 61 | HopLimit = ttl, 62 | NextHdr = ProtocolType.IcmpV6, 63 | Version = IPVersion.V6 64 | }; 65 | var icmpHeader = new IcmpV6Header 66 | { 67 | Type = IcmpV6MessageType.EchoRequest, 68 | Identifier = Id, 69 | SequenceNumber = SeqNum 70 | }; 71 | 72 | var writer = GetWriter(); 73 | writer.Write(ipHeader); 74 | writer.Write(icmpHeader); 75 | writer.Advance(32); 76 | } 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /App/Icmp/RouteTracer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.NetworkInformation; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using WindivertDotnet; 10 | 11 | namespace App.Icmp 12 | { 13 | /// 14 | /// 路由信息 15 | /// 16 | static class RouteTracer 17 | { 18 | private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum); 19 | 20 | /// 21 | /// 测试路由 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | public async static Task TraceAsync(IPAddress dstAddr, byte ttlCount, TimeSpan waitTime, CancellationToken cancellationToken = default) 30 | { 31 | var filter = Filter.True 32 | .And(f => f.Network.Inbound) 33 | .And(f => f.ICmp.Type == IcmpV4MessageType.TimeExceeded || f.IcmpV6.Type == IcmpV6MessageType.TimeExceeded); 34 | 35 | using var divert = new WinDivert(filter, WinDivertLayer.Network); 36 | 37 | // 开始监听ping的回复 38 | using var waitTokenSource = new CancellationTokenSource(); 39 | using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken); 40 | var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token); 41 | 42 | // 开始ttl发ping 43 | var ttls = Enumerable.Range(1, ttlCount).Select(i => (byte)i); 44 | var seqNums = await SendPingsAsync(divert, dstAddr, ttls, cancellationToken); 45 | 46 | // 延时取消监听 47 | waitTokenSource.CancelAfter(waitTime); 48 | var results = await recvTask; 49 | 50 | return results 51 | .Where(item => seqNums.Contains(item.SeqNum)) 52 | .Select(item => item.SrcAddress) 53 | .Distinct() 54 | .ToArray(); 55 | } 56 | 57 | /// 58 | /// 接收回复信息 59 | /// 60 | /// 61 | /// 62 | /// 63 | private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken) 64 | { 65 | var results = new List(); 66 | using var packet = new WinDivertPacket(); 67 | using var addr = new WinDivertAddress(); 68 | 69 | while (cancellationToken.IsCancellationRequested == false) 70 | { 71 | try 72 | { 73 | await divert.RecvAsync(packet, addr, cancellationToken); 74 | 75 | if (TryParseReply(packet, out var srcAddrSeqNum)) 76 | { 77 | results.Add(srcAddrSeqNum); 78 | } 79 | 80 | await divert.SendAsync(packet, addr, cancellationToken); 81 | } 82 | catch (OperationCanceledException) 83 | { 84 | break; 85 | } 86 | } 87 | 88 | return results; 89 | } 90 | 91 | /// 92 | /// 解析出icmp回复信息 93 | /// 94 | /// 95 | /// 96 | /// 97 | private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum) 98 | { 99 | var result = packet.GetParseResult(); 100 | if (result.DataLength > 0) 101 | { 102 | var srcAddr = result.IPV4Header != null 103 | ? result.IPV4Header->SrcAddr 104 | : result.IPV6Header->SrcAddr; 105 | 106 | var dataPacket = packet.Slice(packet.Length - result.DataLength, result.DataLength); 107 | dataPacket.ApplyLengthToHeaders(); 108 | 109 | var dataResult = dataPacket.GetParseResult(); 110 | if (dataResult.IcmpV4Header != null) 111 | { 112 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV4Header->SequenceNumber); 113 | return true; 114 | } 115 | if (dataResult.IcmpV6Header != null) 116 | { 117 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV6Header->SequenceNumber); 118 | return true; 119 | } 120 | } 121 | 122 | srcAddrSeqNum = null; 123 | return false; 124 | } 125 | 126 | /// 127 | /// 发送ping请求包 128 | /// 129 | /// 130 | /// 131 | /// 132 | /// 133 | /// 134 | private async static Task> SendPingsAsync(WinDivert divert, IPAddress dstAddr, IEnumerable ttls, CancellationToken cancellationToken) 135 | { 136 | var router = new WinDivertRouter(dstAddr); 137 | using var addr = router.CreateAddress(); 138 | var result = new HashSet(); 139 | foreach (var ttl in ttls) 140 | { 141 | using var packet = new PingPacket(router, ttl); 142 | packet.CalcChecksums(addr); 143 | await divert.SendAsync(packet, addr, cancellationToken); 144 | result.Add(packet.SeqNum); 145 | } 146 | return result; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /App/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using WindivertDotnet; 4 | 5 | namespace App 6 | { 7 | internal class Program 8 | { 9 | static async Task Main(string[] args) 10 | { 11 | var filter = Filter.True.And(f => f.IsIP && f.IsTcp); 12 | using var divert = new WinDivert(filter, WinDivertLayer.Network); 13 | using var packet = new WinDivertPacket(); 14 | using var addr = new WinDivertAddress(); 15 | 16 | while (true) 17 | { 18 | var recvLength = await divert.RecvAsync(packet, addr); 19 | var result = packet.GetParseResult(); 20 | 21 | var checkState = packet.CalcChecksums(addr); 22 | var sendLength = await divert.SendAsync(packet, addr); 23 | 24 | Console.WriteLine($"{result.Protocol} {recvLength} {sendLength}"); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /App/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 62 | 63 | 64 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WindivertDotnet 2 | 面向对象的[WinDivertv2.2](https://github.com/basil00/Divert)的dotnet异步封装,轻松实现网络数据拦截与修改。 3 | 4 | ### 1 nuget 5 | [WindivertDotnet](https://www.nuget.org/packages/WindivertDotnet) 6 | ``` 7 | 8 | ``` 9 | 10 | ### 2 功能介绍 11 | * 抓取网络数据包 12 | * 过滤或丢弃网络数据包 13 | * 嗅探网络数据包 14 | * 注入网络数据包 15 | * 修改网络数据包 16 | 17 | 18 | ### 3 如何使用 19 | #### 3.1 抓包改包 20 | ```c# 21 | var filter = Filter.True 22 | .And(f => f.Network.Loopback) 23 | .And(f => f.Tcp.DstPort == 443) 24 | .And(f => f.Tcp.Ack == true); 25 | 26 | using var divert = new WinDivert(filter, WinDivertLayer.Network); 27 | using var packet = new WinDivertPacket(); 28 | using var addr = new WinDivertAddress(); 29 | 30 | while (true) 31 | { 32 | // 读包 33 | await divert.RecvAsync(packet, addr); 34 | 35 | ProcessPacket(packet, addr); 36 | 37 | // 修改后发出 38 | await divert.SendAsync(packet, addr); 39 | } 40 | 41 | static unsafe void ProcessPacket(WinDivertPacket packet, WinDivertAddress addr) 42 | { 43 | // 解包 44 | var result = packet.GetParseResult(); 45 | 46 | // 改包 47 | result.TcpHeader->DstPort = 443; 48 | 49 | // 重算checksums 50 | packet.CalcChecksums(addr); 51 | } 52 | ``` 53 | 54 | #### 3.2 注入数据包 55 | ```c# 56 | private async Task SendEchoRequestAsync(IPAddress dstAddr) 57 | { 58 | // 使用router计算将进行通讯的本机地址 59 | var router = new WinDivertRouter(dstAddr); 60 | using var addr = router.CreateAddress(); 61 | using var packet = this.CreateIPV4EchoPacket(router.SrcAddress, router.DstAddress); 62 | 63 | packet.CalcChecksums(addr); // 计算checksums,因为创建包时没有计算 64 | 65 | await this.divert.SendAsync(packet, addr); 66 | } 67 | 68 | /// 69 | /// 创建icmp的echo包 70 | /// 71 | /// 72 | /// 73 | /// 74 | private unsafe WinDivertPacket CreateIPV4EchoPacket(IPAddress srcAddr, IPAddress dstAddr) 75 | { 76 | // ipv4头 77 | var ipHeader = new IPV4Header 78 | { 79 | TTL = 128, 80 | Version = IPVersion.V4, 81 | DstAddr = dstAddr, 82 | SrcAddr = srcAddr, 83 | Protocol = ProtocolType.Icmp, 84 | HdrLength = (byte)(sizeof(IPV4Header) / 4), 85 | Id = ++this.id, 86 | Length = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header)) 87 | }; 88 | 89 | // icmp头 90 | var icmpHeader = new IcmpV4Header 91 | { 92 | Type = IcmpV4MessageType.EchoRequest, 93 | Code = default, 94 | Identifier = ipHeader.Id, 95 | SequenceNumber = ++this.sequenceNumber, 96 | }; 97 | 98 | // 将数据写到packet缓冲区 99 | var packet = new WinDivertPacket(ipHeader.Length); 100 | 101 | var writer = packet.GetWriter(); 102 | writer.Write(ipHeader); 103 | writer.Write(icmpHeader); 104 | 105 | return packet; 106 | } 107 | ``` 108 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/FilterTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | using System.Runtime.Versioning; 4 | using Xunit; 5 | 6 | namespace WindivertDotnet.Test 7 | { 8 | public class FilterTest 9 | { 10 | [Fact] 11 | public void TrueTest() 12 | { 13 | var filter = Filter.True.ToString(); 14 | Assert.Equal("true", filter); 15 | } 16 | 17 | [Fact] 18 | public void FlaseTest() 19 | { 20 | var filter = Filter.False.ToString(); 21 | Assert.Equal("false", filter); 22 | } 23 | 24 | [Fact] 25 | public void AndExpressionTest() 26 | { 27 | var filter = Filter.True.And(f => false).ToString(); 28 | Assert.Equal("false", filter); 29 | } 30 | 31 | [Fact] 32 | public void OrExpressionTest() 33 | { 34 | var filter = Filter.True.Or(f => false).ToString(); 35 | Assert.Equal("true", filter); 36 | } 37 | 38 | [Fact] 39 | public void AndFilterTest() 40 | { 41 | var filter = Filter.True.And(Filter.False).ToString(); 42 | Assert.Equal("false", filter); 43 | } 44 | 45 | [Fact] 46 | public void OrFilterTest() 47 | { 48 | var filter = Filter.True.Or(Filter.False).ToString(); 49 | Assert.Equal("true", filter); 50 | } 51 | 52 | [Fact] 53 | [SupportedOSPlatform("windows")] 54 | public void BaseTest() 55 | { 56 | var filter = Filter.False 57 | .Or(f => f.IsIcmp) 58 | .Or(f => f.IsIcmpV6) 59 | .Or(f => f.IsIP) 60 | .Or(f => f.IsIPV6) 61 | .Or(f => f.IsTcp) 62 | .Or(f => f.IsUdp) 63 | .Or(f => f.Timestamp > 1) 64 | ; 65 | 66 | var f1 = Filter.Compile(filter.ToString(), WinDivertLayer.Network); 67 | var f2 = Filter.Compile(filter.ToString(), WinDivertLayer.Forward); 68 | var f3 = Filter.Compile(filter.ToString(), WinDivertLayer.Socket); 69 | 70 | Assert.True(f1.Length > 0); 71 | Assert.True(f2.Length > 0); 72 | Assert.True(f3.Length > 0); 73 | } 74 | 75 | 76 | [Fact] 77 | [SupportedOSPlatform("windows")] 78 | public void NetworkTest() 79 | { 80 | var filter = Filter.False 81 | .Or(f => f.Event == Event.PACKET) 82 | .Or(f => f.Network.Fragment) 83 | .Or(f => f.Network.Inbound) 84 | .Or(f => f.Network.Length > 0) 85 | .Or(f => f.Network.IfIdx > 0) 86 | .Or(f => f.Network.Impostor) 87 | .Or(f => f.Network.LocalAddr == IPAddress.Loopback.ToString()) 88 | .Or(f => f.Network.LocalPort > 0) 89 | .Or(f => f.Network.Loopback) 90 | .Or(f => f.Network.Outbound) 91 | .Or(f => f.Network.Protocol == ProtocolType.Udp) 92 | .Or(f => f.Network.RemoteAddr == IPAddress.Loopback.ToString()) 93 | .Or(f => f.Network.RemotePort == 443) 94 | .Or(f => f.Network.SubIfIdx == 12); 95 | 96 | var f = Filter.Compile(filter.ToString(), WinDivertLayer.Network); 97 | Assert.True(f.Length > 0); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /WindivertDotnet.Test/HeaderSizeTest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Xunit; 3 | 4 | namespace WindivertDotnet.Test 5 | { 6 | public class HeaderSizeTest 7 | { 8 | [Fact] 9 | public void IPV4HeaderSizeTest() 10 | { 11 | Assert.Equal(20, Unsafe.SizeOf()); 12 | } 13 | 14 | [Fact] 15 | public void IPV6HeaderSizeTest() 16 | { 17 | Assert.Equal(40, Unsafe.SizeOf()); 18 | } 19 | 20 | [Fact] 21 | public void IcmpV4HeaderSizeTest() 22 | { 23 | Assert.Equal(8, Unsafe.SizeOf()); 24 | } 25 | 26 | [Fact] 27 | public void IcmpV6HeaderSizeTest() 28 | { 29 | Assert.Equal(8, Unsafe.SizeOf()); 30 | } 31 | 32 | [Fact] 33 | public void TcpHeaderHeaderSizeTest() 34 | { 35 | Assert.Equal(20, Unsafe.SizeOf()); 36 | } 37 | 38 | [Fact] 39 | public void UdpHeaderHeaderSizeTest() 40 | { 41 | Assert.Equal(8, Unsafe.SizeOf()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/IPHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Security; 3 | using System.Net.Sockets; 4 | using Xunit; 5 | 6 | namespace WindivertDotnet.Test 7 | { 8 | public class IPHeaderTest 9 | { 10 | [Fact] 11 | public unsafe void IPV4ReadTest() 12 | { 13 | var packet = PacketDotNet.IPPacket.RandomPacket(PacketDotNet.IPVersion.IPv4); 14 | fixed (void* ptr = &packet.Bytes[0]) 15 | { 16 | var header = *(IPV4Header*)ptr; 17 | Assert.Equal((byte)packet.Version, (byte)header.Version); 18 | Assert.Equal(packet.HeaderLength, header.HdrLength); 19 | Assert.Equal(packet.TotalLength, header.Length); 20 | Assert.Equal(packet.TimeToLive, header.TTL); 21 | Assert.Equal((byte)packet.Protocol, (byte)header.Protocol); 22 | Assert.Equal(packet.SourceAddress, header.SrcAddr); 23 | Assert.Equal(packet.DestinationAddress, header.DstAddr); 24 | } 25 | } 26 | 27 | [Fact] 28 | public unsafe void IPV4WriteTest() 29 | { 30 | var dstAddr = IPAddress.Parse("1.2.3.4"); 31 | var srcAddr = IPAddress.Parse("5.6.7.8"); 32 | 33 | var header = new IPV4Header 34 | { 35 | Version = IPVersion.V4, 36 | Checksum = 2, 37 | DstAddr = dstAddr, 38 | SrcAddr = srcAddr, 39 | TOS = 3, 40 | HdrLength = 5, 41 | Id = 6, 42 | Length = 7, 43 | Protocol = ProtocolType.Ggp, 44 | TTL = 8, 45 | FragmentFlags = FragmentFlag.MoreFragments, 46 | FragmentOffset = 9, 47 | }; 48 | 49 | 50 | Assert.Equal(IPVersion.V4, header.Version); 51 | Assert.Equal(2, header.Checksum); 52 | 53 | Assert.Equal(dstAddr, header.DstAddr); 54 | Assert.Equal(srcAddr, header.SrcAddr); 55 | Assert.Equal(3, header.TOS); 56 | Assert.Equal(5, header.HdrLength); 57 | Assert.Equal(6, header.Id); 58 | 59 | Assert.Equal(7, header.Length); 60 | Assert.Equal(ProtocolType.Ggp, header.Protocol); 61 | Assert.Equal(8, header.TTL); 62 | 63 | Assert.Equal(FragmentFlag.MoreFragments, header.FragmentFlags); 64 | Assert.Equal(9, header.FragmentOffset); 65 | } 66 | 67 | [Fact] 68 | public unsafe void IPV6ReadTest() 69 | { 70 | var packet = PacketDotNet.IPPacket.RandomPacket(PacketDotNet.IPVersion.IPv6); 71 | fixed (void* ptr = &packet.Bytes[0]) 72 | { 73 | var header = *(IPV6Header*)ptr; 74 | Assert.Equal((byte)packet.Version, (byte)header.Version); 75 | Assert.Equal(packet.PayloadLength, header.Length); 76 | Assert.Equal(packet.HopLimit, header.HopLimit); 77 | Assert.Equal((byte)packet.Protocol, (byte)header.NextHdr); 78 | Assert.Equal(packet.SourceAddress, header.SrcAddr); 79 | Assert.Equal(packet.DestinationAddress, header.DstAddr); 80 | } 81 | } 82 | 83 | [Fact] 84 | public unsafe void IPV6WriteTest() 85 | { 86 | var dstAddr = IPAddress.Parse("2409:895a:f138:8189:3c42:fd9f:8ee6:4041"); 87 | var srcAddr = IPAddress.IPv6Loopback; 88 | 89 | var header = new IPV6Header 90 | { 91 | Version = IPVersion.V6, 92 | FlowLabel0 = 2, 93 | FlowLabel1 = 3, 94 | HopLimit = 4, 95 | Length = 0, 96 | TrafficClass0 = 6, 97 | TrafficClass1 = 7, 98 | DstAddr = dstAddr, 99 | SrcAddr = srcAddr, 100 | NextHdr = ProtocolType.Ggp, 101 | 102 | }; 103 | 104 | Assert.Equal(IPVersion.V6, header.Version); 105 | Assert.Equal(2, header.FlowLabel0); 106 | 107 | Assert.Equal(dstAddr, header.DstAddr); 108 | Assert.Equal(srcAddr, header.SrcAddr); 109 | Assert.Equal(3, header.FlowLabel1); 110 | Assert.Equal(4, header.HopLimit); 111 | Assert.Equal(0, header.Length); 112 | Assert.Equal(6, header.TrafficClass0); 113 | 114 | Assert.Equal(7, header.TrafficClass1); 115 | Assert.Equal(ProtocolType.Ggp, header.NextHdr); 116 | 117 | using var packet = new WinDivertPacket(); 118 | packet.GetWriter().Write(header); 119 | var r = packet.GetParseResult(); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/IcmpV4HeaderTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace WindivertDotnet.Test 4 | { 5 | public unsafe class IcmpV4HeaderTest 6 | { 7 | [Fact] 8 | public void ReadWriteTest() 9 | { 10 | var header = new IcmpV4Header 11 | { 12 | Checksum = 1, 13 | Code = IcmpV4UnreachableCode.PrecedenceCutoffInEffect, 14 | Type = IcmpV4MessageType.TimeExceeded, 15 | Body = 0x20003 16 | }; 17 | 18 | Assert.Equal(1, header.Checksum); 19 | Assert.Equal(0x2, header.Identifier); 20 | Assert.Equal(0x3, header.SequenceNumber); 21 | Assert.Equal(0x20003, (int)header.Body); 22 | Assert.Equal(IcmpV4UnreachableCode.PrecedenceCutoffInEffect, header.Code); 23 | Assert.Equal(IcmpV4MessageType.TimeExceeded, header.Type); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/IcmpV6HeaderTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace WindivertDotnet.Test 4 | { 5 | public unsafe class IcmpV6HeaderTest 6 | { 7 | [Fact] 8 | public void ReadWriteTest() 9 | { 10 | var header = new IcmpV6Header 11 | { 12 | Checksum = 1, 13 | Code = IcmpV6UnreachableCode.PortUnreachable, 14 | Type = IcmpV6MessageType.EchoRequest, 15 | Body = 0x20003 16 | }; 17 | 18 | Assert.Equal(1, header.Checksum); 19 | Assert.Equal(0x2, header.Identifier); 20 | Assert.Equal(0x3, header.SequenceNumber); 21 | Assert.Equal((uint)0x20003, header.Body); 22 | Assert.Equal(IcmpV6UnreachableCode.PortUnreachable, header.Code); 23 | Assert.Equal(IcmpV6MessageType.EchoRequest, header.Type); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/TcpHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace WindivertDotnet.Test 4 | { 5 | public unsafe class TcpHeaderTest 6 | { 7 | [Fact] 8 | public void ReadTest() 9 | { 10 | var packet = PacketDotNet.TcpPacket.RandomPacket(); 11 | fixed (void* ptr = &packet.Bytes[0]) 12 | { 13 | var header = *(TcpHeader*)ptr; 14 | Assert.Equal(packet.SourcePort, header.SrcPort); 15 | Assert.Equal(packet.Finished, header.Fin); 16 | Assert.Equal(packet.Checksum, header.Checksum); 17 | Assert.Equal(packet.DestinationPort, header.DstPort); 18 | Assert.Equal(packet.Synchronize, header.Syn); 19 | Assert.Equal(packet.SequenceNumber, header.SeqNum); 20 | Assert.Equal(packet.WindowSize, header.Window); 21 | Assert.Equal(packet.Acknowledgment, header.Ack); 22 | Assert.Equal(packet.AcknowledgmentNumber, header.AckNum); 23 | Assert.Equal(packet.Reset, header.Rst); 24 | Assert.Equal(packet.Push, header.Psh); 25 | Assert.Equal(packet.Urgent, header.Urg); 26 | Assert.Equal(packet.UrgentPointer, header.UrgPtr); 27 | } 28 | } 29 | 30 | [Fact] 31 | public void WriteTest() 32 | { 33 | var header = new TcpHeader 34 | { 35 | Ack = true, 36 | Fin = true, 37 | Psh = true, 38 | Urg = true, 39 | Rst = true, 40 | 41 | 42 | 43 | AckNum = 2, 44 | Checksum = 3, 45 | DstPort = 4, 46 | HdrLength = 6, 47 | UrgPtr = 8, 48 | 49 | SeqNum = 12, 50 | SrcPort = 13, 51 | Window = 16 52 | }; 53 | 54 | Assert.True(header.Flags.HasFlag(TcpFlag.Ack)); 55 | Assert.True(header.Flags.HasFlag(TcpFlag.Fin)); 56 | Assert.True(header.Flags.HasFlag(TcpFlag.Psh)); 57 | Assert.True(header.Flags.HasFlag(TcpFlag.Rst)); 58 | Assert.True(header.Flags.HasFlag(TcpFlag.Fin)); 59 | Assert.False(header.Flags.HasFlag(TcpFlag.Syn)); 60 | 61 | Assert.Equal(2U, header.AckNum); 62 | Assert.Equal(3, header.Checksum); 63 | Assert.Equal(4, header.DstPort); 64 | Assert.Equal(6, header.HdrLength); 65 | Assert.Equal(8, header.UrgPtr); 66 | 67 | Assert.Equal(12U, header.SeqNum); 68 | Assert.Equal(13, header.SrcPort); 69 | Assert.Equal(16, header.Window); 70 | 71 | 72 | header.Flags &= ~(TcpFlag.Fin | TcpFlag.Rst); 73 | Assert.False(header.Flags.HasFlag(TcpFlag.Rst)); 74 | Assert.False(header.Flags.HasFlag(TcpFlag.Fin)); 75 | 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/UdpHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace WindivertDotnet.Test 9 | { 10 | public unsafe class UdpHeaderTest 11 | { 12 | [Fact] 13 | public void ReadTest() 14 | { 15 | var packet = PacketDotNet.UdpPacket.RandomPacket(); 16 | fixed (void* ptr = &packet.Bytes[0]) 17 | { 18 | var header = *(UdpHeader*)ptr; 19 | Assert.Equal(packet.SourcePort, header.SrcPort); 20 | Assert.Equal(packet.Length, header.Length); 21 | Assert.Equal(packet.Checksum, header.Checksum); 22 | Assert.Equal(packet.DestinationPort, header.DstPort); 23 | } 24 | } 25 | 26 | [Fact] 27 | public void WriteTest() 28 | { 29 | var header = new UdpHeader 30 | { 31 | Checksum = 1, 32 | DstPort = 2, 33 | SrcPort = 3, 34 | Length = 4, 35 | }; 36 | Assert.Equal(1, header.Checksum); 37 | Assert.Equal(2, header.DstPort); 38 | Assert.Equal(3, header.SrcPort); 39 | Assert.Equal(4, header.Length); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WinDivertAddressTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace WindivertDotnet.Test 4 | { 5 | public class WinDivertAddressTest 6 | { 7 | [Fact] 8 | public void CloneTest() 9 | { 10 | using var addr = new WinDivertAddress(); 11 | addr.Flags = WinDivertAddressFlag.Loopback; 12 | 13 | using var clone = addr.Clone(); 14 | Assert.Equal(addr.Flags, clone.Flags); 15 | 16 | addr.Flags = WinDivertAddressFlag.IPv6; 17 | Assert.NotEqual(addr.Flags, clone.Flags); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WinDivertBufferWriterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using Xunit; 6 | 7 | namespace WindivertDotnet.Test 8 | { 9 | public class WinDivertBufferWriterTest 10 | { 11 | [Fact] 12 | public void WriteByteTest() 13 | { 14 | using var packet = new WinDivertPacket(); 15 | var writer = packet.GetWriter(); 16 | writer.Write(byte.MaxValue); 17 | Assert.Equal(1, packet.Length); 18 | Assert.Equal(byte.MaxValue, packet.Span[0]); 19 | } 20 | 21 | 22 | [Fact] 23 | public void WriteSpanTest() 24 | { 25 | using var packet = new WinDivertPacket(); 26 | var writer = packet.GetWriter(); 27 | writer.Write(new byte[] { 1, 2 }); 28 | Assert.Equal(2, packet.Length); 29 | Assert.True(packet.Span.SequenceEqual(new byte[] { 1, 2 })); 30 | } 31 | 32 | 33 | [Fact] 34 | public void WriteInt32Test() 35 | { 36 | using var packet = new WinDivertPacket(); 37 | var writer = packet.GetWriter(); 38 | writer.Write(100); 39 | Assert.Equal(sizeof(int), packet.Length); 40 | Assert.Equal(100, BitConverter.ToInt32(packet.Span)); 41 | } 42 | 43 | [Fact] 44 | public void WriteMyStructTest() 45 | { 46 | using var packet = new WinDivertPacket(); 47 | var writer = packet.GetWriter(); 48 | var myStruct = new MyStruct { Byte1 = 1, Byte2 = 2 }; 49 | writer.Write(myStruct); 50 | 51 | Assert.Equal(Unsafe.SizeOf(), packet.Length); 52 | Assert.True(packet.Span[0] == 1 && packet.Span[1] == 2); 53 | } 54 | 55 | 56 | [Fact] 57 | public void WriteReverseInt32Test() 58 | { 59 | using var packet = new WinDivertPacket(); 60 | var writer = packet.GetWriter(); 61 | writer.WriteReverse(100); 62 | Assert.Equal(sizeof(int), packet.Length); 63 | Assert.Equal(BinaryPrimitives.ReverseEndianness(100), BitConverter.ToInt32(packet.Span)); 64 | } 65 | 66 | [Fact] 67 | public void WriteReverseMyStructTest() 68 | { 69 | using var packet = new WinDivertPacket(); 70 | var writer = packet.GetWriter(); 71 | var myStruct = new MyStruct { Byte1 = 1, Byte2 = 2 }; 72 | writer.WriteReverse(myStruct); 73 | 74 | Assert.Equal(Unsafe.SizeOf(), packet.Length); 75 | Assert.True(packet.Span[0] == 2 && packet.Span[1] == 1); 76 | } 77 | 78 | [Fact] 79 | public void OffsetLengthTest() 80 | { 81 | using var packet = new WinDivertPacket(); 82 | var writer = packet.GetWriter(10); 83 | writer.Advance(8); 84 | 85 | Assert.Equal(18, packet.Length); 86 | } 87 | 88 | struct MyStruct 89 | { 90 | public byte Byte1; 91 | public byte Byte2; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WinDivertPacketExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Runtime.Versioning; 5 | using Xunit; 6 | 7 | namespace WindivertDotnet.Test 8 | { 9 | [SupportedOSPlatform("windows")] 10 | public class WinDivertPacketExtensionsTest 11 | { 12 | [Fact] 13 | public void CloneTest() 14 | { 15 | using var packet = new WinDivertPacket(); 16 | packet.GetWriter().Write(Random.Shared.Next()); 17 | 18 | using var clone = packet.Clone(); 19 | Assert.False(ReferenceEquals(packet, clone)); 20 | Assert.Equal(packet, clone); 21 | } 22 | 23 | [Fact] 24 | public void CopyToTest() 25 | { 26 | using var packet = new WinDivertPacket(); 27 | packet.GetWriter().Write(Random.Shared.Next()); 28 | 29 | using var dstPacket = new WinDivertPacket(100); 30 | packet.CopyTo(dstPacket); 31 | Assert.Equal(packet, dstPacket); 32 | 33 | using var emptyPacket = new WinDivertPacket(0); 34 | Assert.Throws(() => packet.CopyTo(emptyPacket)); 35 | } 36 | 37 | [Fact] 38 | [SupportedOSPlatform("windows")] 39 | public unsafe void IPV4TcpReverseEndPointTest() 40 | { 41 | var ipHeader = new IPV4Header 42 | { 43 | Version = IPVersion.V4, 44 | HdrLength = 5, 45 | Protocol = ProtocolType.Tcp, 46 | Length = 40, 47 | SrcAddr = IPAddress.Loopback, 48 | DstAddr = IPAddress.Parse("1.1.1.1") 49 | }; 50 | var tcpHeader = new TcpHeader 51 | { 52 | HdrLength = 5, 53 | SrcPort = 1234, 54 | DstPort = 443 55 | }; 56 | 57 | using var packet = new WinDivertPacket(); 58 | var writer = packet.GetWriter(); 59 | writer.Write(ipHeader); 60 | writer.Write(tcpHeader); 61 | 62 | var state = packet.ReverseEndPoint(); 63 | var result = packet.GetParseResult(); 64 | 65 | Assert.True(state); 66 | Assert.Equal(ipHeader.SrcAddr, result.IPV4Header->DstAddr); 67 | Assert.Equal(ipHeader.DstAddr, result.IPV4Header->SrcAddr); 68 | Assert.Equal(tcpHeader.SrcPort, result.TcpHeader->DstPort); 69 | Assert.Equal(tcpHeader.DstPort, result.TcpHeader->SrcPort); 70 | } 71 | 72 | [Fact] 73 | public unsafe void IPV6UdpReverseEndPointTest() 74 | { 75 | var ipHeader = new IPV6Header 76 | { 77 | Version = IPVersion.V6, 78 | NextHdr = ProtocolType.Udp, 79 | Length = (ushort)(sizeof(UdpHeader)), 80 | SrcAddr = IPAddress.IPv6Loopback, 81 | DstAddr = IPAddress.Parse("fe80::3d4d:f188:49a5:36c6%12") 82 | }; 83 | var udpHeader = new UdpHeader 84 | { 85 | Length = 8, 86 | SrcPort = 1234, 87 | DstPort = 443 88 | }; 89 | 90 | using var packet = new WinDivertPacket(); 91 | var writer = packet.GetWriter(); 92 | writer.Write(ipHeader); 93 | writer.Write(udpHeader); 94 | 95 | var state = packet.ReverseEndPoint(); 96 | var result = packet.GetParseResult(); 97 | 98 | Assert.True(state); 99 | Assert.Equal(ipHeader.SrcAddr, result.IPV6Header->DstAddr); 100 | Assert.Equal(ipHeader.DstAddr, result.IPV6Header->SrcAddr); 101 | Assert.Equal(udpHeader.SrcPort, result.UdpHeader->DstPort); 102 | Assert.Equal(udpHeader.DstPort, result.UdpHeader->SrcPort); 103 | } 104 | 105 | 106 | [Fact] 107 | public unsafe void ApplyLengthToHeadersIPv4Test() 108 | { 109 | var ipHeader = new IPV4Header 110 | { 111 | Version = IPVersion.V4, 112 | HdrLength = 5, 113 | }; 114 | 115 | using var packet = new WinDivertPacket(); 116 | packet.GetWriter().Write(ipHeader); 117 | var count = packet.ApplyLengthToHeaders(); 118 | var result = packet.GetParseResult(); 119 | Assert.Equal(1, count); 120 | Assert.Equal(packet.Length, result.IPV4Header->Length); 121 | 122 | packet.Length += 10; 123 | result = packet.GetParseResult(); 124 | count = packet.ApplyLengthToHeaders(); 125 | Assert.Equal(1, count); 126 | Assert.Equal(packet.Length, result.IPV4Header->Length); 127 | } 128 | 129 | [Fact] 130 | public unsafe void ApplyLengthToHeadersIPv4UdpTest() 131 | { 132 | var ipHeader = new IPV4Header 133 | { 134 | Version = IPVersion.V4, 135 | HdrLength = 5, 136 | Protocol = ProtocolType.Udp, 137 | }; 138 | 139 | var udpHeader = new UdpHeader(); 140 | 141 | 142 | using var packet = new WinDivertPacket(); 143 | var writer = packet.GetWriter(); 144 | writer.Write(ipHeader); 145 | writer.Write(udpHeader); 146 | writer.Advance(255); 147 | 148 | var count = packet.ApplyLengthToHeaders(); 149 | var result = packet.GetParseResult(); 150 | 151 | Assert.Equal(2, count); 152 | Assert.Equal(packet.Length, result.IPV4Header->Length); 153 | Assert.Equal(packet.Length - sizeof(IPV4Header), result.UdpHeader->Length); 154 | } 155 | 156 | [Fact] 157 | public unsafe void ApplyLengthToHeadersIPv6Test() 158 | { 159 | var ipHeader = new IPV6Header 160 | { 161 | Version = IPVersion.V6, 162 | }; 163 | 164 | using var packet = new WinDivertPacket(); 165 | packet.GetWriter().Write(ipHeader); 166 | var count = packet.ApplyLengthToHeaders(); 167 | var result = packet.GetParseResult(); 168 | Assert.Equal(1, count); 169 | Assert.Equal(packet.Length - sizeof(IPV6Header), result.IPV6Header->Length); 170 | 171 | packet.Length += 10; 172 | result = packet.GetParseResult(); 173 | count = packet.ApplyLengthToHeaders(); 174 | Assert.Equal(1, count); 175 | Assert.Equal(packet.Length - sizeof(IPV6Header), result.IPV6Header->Length); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WinDivertPacketTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Runtime.Versioning; 5 | using Xunit; 6 | 7 | namespace WindivertDotnet.Test 8 | { 9 | [SupportedOSPlatform("windows")] 10 | public class WinDivertPacketTest 11 | { 12 | [Fact] 13 | public void CtorTest() 14 | { 15 | using var packet = new WinDivertPacket(10); 16 | Assert.Equal(10, packet.Capacity); 17 | Assert.Equal(0, packet.Length); 18 | Assert.Equal(0, packet.Span.Length); 19 | } 20 | 21 | [Fact] 22 | public void LengthTest() 23 | { 24 | using var packet = new WinDivertPacket(10); 25 | packet.Length = 5; 26 | Assert.Equal(10, packet.Capacity); 27 | Assert.Equal(5, packet.Length); 28 | Assert.Equal(5, packet.Span.Length); 29 | 30 | Assert.Throws(() => packet.Length = 12); 31 | } 32 | 33 | [Fact] 34 | public void GetSpanTest() 35 | { 36 | using var packet = new WinDivertPacket(10); 37 | packet.Length = 1; 38 | Assert.Equal(0, packet.GetSpan(0, 10)[0]); 39 | Assert.Throws(() => packet.GetSpan(1, 10)); 40 | } 41 | 42 | 43 | [Fact] 44 | public void SliceTest() 45 | { 46 | using var packet = new WinDivertPacket(10); 47 | var slicePacket = packet.Slice(2, 8); 48 | 49 | var span = packet.GetSpan(0, packet.Capacity); 50 | span[1] = 1; 51 | span[2] = 2; 52 | span[4] = 4; 53 | span[8] = 8; 54 | 55 | Assert.Equal(8, slicePacket.Capacity); 56 | Assert.Equal(8, slicePacket.Length); 57 | Assert.True(span.Slice(2, 8).SequenceEqual(slicePacket.Span)); 58 | } 59 | 60 | [Fact] 61 | public unsafe void CalcNetworkIfIdxTest() 62 | { 63 | var router = new WinDivertRouter(IPAddress.Loopback); 64 | using var addr = new WinDivertAddress(); 65 | 66 | using var packet = new WinDivertPacket(); 67 | var ipv4Header = CreateIPV4Header(router); 68 | packet.GetWriter().Write(ipv4Header); 69 | 70 | packet.CalcNetworkIfIdx(addr); 71 | Assert.Equal(router.InterfaceIndex, addr.Network->IfIdx); 72 | } 73 | 74 | [Fact] 75 | public void CalcLoopbackFlagTest() 76 | { 77 | var router = new WinDivertRouter(IPAddress.Loopback); 78 | using var addr = new WinDivertAddress(); 79 | 80 | using var packet = new WinDivertPacket(); 81 | var ipv4Header = CreateIPV4Header(router); 82 | packet.GetWriter().Write(ipv4Header); 83 | 84 | packet.CalcLoopbackFlag(addr); 85 | Assert.Equal(router.IsLoopback, addr.Flags.HasFlag(WinDivertAddressFlag.Loopback)); 86 | } 87 | 88 | 89 | [Fact] 90 | public void CalcOutboundFlagTest() 91 | { 92 | var router = new WinDivertRouter(IPAddress.Loopback); 93 | using var addr = new WinDivertAddress(); 94 | 95 | using var packet = new WinDivertPacket(); 96 | var ipv4Header = CreateIPV4Header(router); 97 | packet.GetWriter().Write(ipv4Header); 98 | 99 | packet.CalcOutboundFlag(addr); 100 | Assert.Equal(router.IsOutbound, addr.Flags.HasFlag(WinDivertAddressFlag.Outbound)); 101 | } 102 | 103 | [Fact] 104 | public void EqualsTest() 105 | { 106 | using var p1 = new WinDivertPacket(); 107 | using var p2 = new WinDivertPacket(); 108 | p1.GetWriter().Write(1); 109 | p2.GetWriter().Write(1); 110 | 111 | Assert.Equal(p1, p2); 112 | 113 | p1.GetWriter().Write(3); 114 | p2.GetWriter().Write(4); 115 | 116 | Assert.NotEqual(p1, p2); 117 | } 118 | 119 | 120 | private static IPV4Header CreateIPV4Header(WinDivertRouter router) 121 | { 122 | return new IPV4Header 123 | { 124 | Version = IPVersion.V4, 125 | HdrLength = 5, 126 | Length = 20, 127 | Protocol = ProtocolType.IP, 128 | SrcAddr = router.SrcAddress, 129 | DstAddr = router.DstAddress 130 | }; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WindivertDotnet.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | True 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /WindivertDotnet.Test/WindivertRouterTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Runtime.Versioning; 3 | using Xunit; 4 | 5 | namespace WindivertDotnet.Test 6 | { 7 | [SupportedOSPlatform("windows")] 8 | public class WindivertRouterTest 9 | { 10 | [Fact] 11 | public void IPv4LoopbackTest() 12 | { 13 | var router = new WinDivertRouter(IPAddress.Loopback); 14 | Assert.Equal(IPAddress.Loopback, router.SrcAddress); 15 | Assert.Equal(IPAddress.Loopback, router.DstAddress); 16 | Assert.True(router.IsOutbound); 17 | Assert.True(router.IsLoopback); 18 | } 19 | 20 | [Fact] 21 | public unsafe void IPv4LoopbackAddrTest() 22 | { 23 | var router = new WinDivertRouter(IPAddress.Loopback); 24 | using var addr = router.CreateAddress(); 25 | 26 | Assert.Equal(addr.Network->IfIdx, router.InterfaceIndex); 27 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Outbound)); 28 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Loopback)); 29 | } 30 | 31 | [Fact] 32 | public void IPv6LoopbackTest() 33 | { 34 | var router = new WinDivertRouter(IPAddress.IPv6Loopback); 35 | Assert.Equal(IPAddress.IPv6Loopback, router.SrcAddress); 36 | Assert.Equal(IPAddress.IPv6Loopback, router.DstAddress); 37 | Assert.True(router.IsOutbound); 38 | Assert.True(router.IsLoopback); 39 | } 40 | 41 | [Fact] 42 | public void IPv41111Test() 43 | { 44 | var dstAddrr = IPAddress.Parse("1.1.1.1"); 45 | var router = new WinDivertRouter(dstAddrr); 46 | Assert.NotEqual(IPAddress.Any, router.SrcAddress); 47 | Assert.Equal(dstAddrr, router.DstAddress); 48 | Assert.True(router.IsOutbound); 49 | Assert.False(router.IsLoopback); 50 | } 51 | 52 | [Fact] 53 | public unsafe void IPv41111AddrTest() 54 | { 55 | var dstAddrr = IPAddress.Parse("1.1.1.1"); 56 | var router = new WinDivertRouter(dstAddrr); 57 | 58 | using var addr = new WinDivertAddress 59 | { 60 | Flags = WinDivertAddressFlag.Loopback 61 | }; 62 | router.ApplyToAddress(addr); 63 | 64 | Assert.Equal(addr.Network->IfIdx, router.InterfaceIndex); 65 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Outbound)); 66 | Assert.False(addr.Flags.HasFlag(WinDivertAddressFlag.Loopback)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /WindivertDotnet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.32912.340 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindivertDotnet", "WindivertDotnet\WindivertDotnet.csproj", "{070A2A01-6E1E-4094-9433-C62E65DBD2FD}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "App\App.csproj", "{9EA97509-CBC0-414E-9C76-283AAA379347}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindivertDotnet.Test", "WindivertDotnet.Test\WindivertDotnet.Test.csproj", "{CD236CAC-7444-458B-8C13-2CEF2CC8333C}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {23DBE1F7-831E-4D48-93C0-BA0A7596A3F6} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /WindivertDotnet/ChecksumsFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// 校验和选项 7 | /// 8 | [Flags] 9 | public enum ChecksumsFlag : ulong 10 | { 11 | /// 12 | /// 全部 13 | /// 14 | All = 0, 15 | 16 | /// 17 | /// 排除IpChecksum 18 | /// 19 | NoIpChecksum = 1, 20 | 21 | /// 22 | /// 排除IcmpChecksum 23 | /// 24 | NoIcmpChecksum = 2, 25 | 26 | /// 27 | /// 排除IcmpV6Checksum 28 | /// 29 | NoIcmpV6Checksum = 4, 30 | 31 | /// 32 | /// 排除TcpChecksum 33 | /// 34 | NoTcpChecksum = 8, 35 | 36 | /// 37 | /// 排除UdpChecksum 38 | /// 39 | NoUdpChecksum = 16, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /WindivertDotnet/Event.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// 事件类型 5 | /// 6 | public sealed class Event 7 | { 8 | private readonly string name; 9 | 10 | private Event(string name) 11 | { 12 | this.name = name; 13 | } 14 | 15 | /// 16 | /// 转换为文本 17 | /// 18 | /// 19 | public override string ToString() 20 | { 21 | return this.name; 22 | } 23 | 24 | /// 25 | /// 新的网络数据包 26 | /// 27 | public static Event PACKET { get; } = new("PACKET"); 28 | 29 | /// 30 | /// 将创建一个新流 31 | /// 32 | public static Event ESTABLISHED { get; } = new("ESTABLISHED"); 33 | 34 | /// 35 | /// 旧流将被删除 36 | /// 37 | public static Event DELETED { get; } = new("DELETED"); 38 | 39 | /// 40 | /// bind操作 41 | /// 42 | public static Event BIND { get; } = new("BIND"); 43 | 44 | /// 45 | /// connect操作 46 | /// 47 | public static Event CONNECT { get; } = new("CONNECT"); 48 | 49 | /// 50 | /// listen操作 51 | /// 52 | public static Event LISTEN { get; } = new("LISTEN"); 53 | 54 | /// 55 | /// accept操作 56 | /// 57 | public static Event ACCEPT { get; } = new("ACCEPT"); 58 | 59 | /// 60 | /// 打开了新的WinDivert实例 61 | /// 62 | public static Event OPEN { get; } = new("OPEN"); 63 | 64 | /// 65 | /// 旧的WinDivert实例被关闭或套接字终结点已关闭 66 | /// 67 | public static Event CLOSE { get; } = new("CLOSE"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /WindivertDotnet/FragmentFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// IPv4标记位 7 | /// 8 | [Flags] 9 | public enum FragmentFlag : byte 10 | { 11 | /// 12 | /// 有分片 13 | /// 14 | MoreFragments = 0x1, 15 | 16 | /// 17 | /// 无分片 18 | /// 19 | DontFragment = 0x02, 20 | 21 | /// 22 | /// 保护 23 | /// 24 | Reserved = 0x04, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WindivertDotnet/IFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | namespace WindivertDotnet 5 | { 6 | /// 7 | /// 定义filter的属性 8 | /// 9 | public interface IFilter 10 | { 11 | /// 12 | /// 发生的时间戳 13 | /// 14 | [FilterMember("timestamp")] 15 | long Timestamp { get; } 16 | 17 | /// 18 | /// 事件 19 | /// 20 | [FilterMember("event")] 21 | Event Event { get; } 22 | 23 | /// 24 | /// 传入/传出本地计算机的网络数据包 25 | /// 26 | INetwork Network { get; } 27 | 28 | /// 29 | /// 通过本地计算机的网络数据包 30 | /// 31 | IForward Forward { get; } 32 | 33 | /// 34 | /// 网络流已建立/已删除事件 35 | /// 36 | IFlow Flow { get; } 37 | 38 | /// 39 | /// 套接字操作事件 40 | /// 41 | ISocket Socket { get; } 42 | 43 | /// 44 | /// WinDivert处理事件 45 | /// 46 | IReflect Reflect { get; } 47 | 48 | /// 49 | /// 是否为IPv4 50 | /// 51 | [FilterMember("ip")] 52 | bool IsIP { get; } 53 | 54 | /// 55 | /// IPv4的属性集 56 | /// 57 | [FilterMember("ip")] 58 | IIP IP { get; } 59 | 60 | /// 61 | /// 是否IPv6 62 | /// 63 | [FilterMember("ipv6")] 64 | bool IsIPV6 { get; } 65 | 66 | /// 67 | /// IPv6属性集 68 | /// 69 | [FilterMember("ipv6")] 70 | IIPV6 IPV6 { get; } 71 | 72 | /// 73 | /// 是否为tcp协议 74 | /// 75 | [FilterMember("tcp")] 76 | bool IsTcp { get; } 77 | 78 | /// 79 | /// tcp属性集 80 | /// 81 | [FilterMember("tcp")] 82 | ITcp Tcp { get; } 83 | 84 | 85 | /// 86 | /// 是否udp协议 87 | /// 88 | [FilterMember("udp")] 89 | bool IsUdp { get; } 90 | 91 | /// 92 | /// udp属性集 93 | /// 94 | [FilterMember("udp")] 95 | IUdp Udp { get; } 96 | 97 | 98 | /// 99 | /// 是否为icmpV4协议 100 | /// 101 | [FilterMember("icmp")] 102 | bool IsIcmp { get; } 103 | 104 | /// 105 | /// icmpV4属性集 106 | /// 107 | [FilterMember("icmp")] 108 | public IICmp ICmp { get; } 109 | 110 | /// 111 | /// 是否为icmpV6协议 112 | /// 113 | [FilterMember("icmpv6")] 114 | bool IsIcmpV6 { get; } 115 | 116 | /// 117 | /// icmpV6属性集 118 | /// 119 | [FilterMember("icmpv6")] 120 | public IICmpV6 IcmpV6 { get; } 121 | 122 | 123 | /// 124 | /// 传入/传出本地计算机的网络数据包 125 | /// 126 | public interface INetwork 127 | { 128 | /// 129 | /// 是否为出口 130 | /// 131 | [FilterMember("outbound")] 132 | bool Outbound { get; } 133 | 134 | /// 135 | /// 是否为入口 136 | /// 137 | [FilterMember("inbound")] 138 | bool Inbound { get; } 139 | 140 | /// 141 | /// 回环IP 142 | /// 143 | [FilterMember("loopback")] 144 | bool Loopback { get; } 145 | 146 | /// 147 | /// 是否已修改 148 | /// 149 | [FilterMember("impostor")] 150 | bool Impostor { get; } 151 | 152 | /// 153 | /// 是否分片 154 | /// 155 | [FilterMember("fragment")] 156 | bool Fragment { get; } 157 | 158 | /// 159 | /// 网卡索引 160 | /// 161 | [FilterMember("ifIdx")] 162 | int IfIdx { get; } 163 | 164 | /// 165 | /// 子网卡索引 166 | /// 167 | [FilterMember("subIfIdx")] 168 | int SubIfIdx { get; } 169 | 170 | /// 171 | /// 协议类型 172 | /// ProtocolType 173 | /// 174 | [FilterMember("protocol")] 175 | ProtocolType Protocol { get; } 176 | 177 | /// 178 | /// 本机IP地址 179 | /// 180 | [FilterMember("localAddr")] 181 | string LocalAddr { get; } 182 | 183 | /// 184 | /// 本机端口 185 | /// 186 | [FilterMember("localPort")] 187 | int LocalPort { get; } 188 | 189 | /// 190 | /// 远程IP地址 191 | /// 192 | [FilterMember("remoteAddr")] 193 | string RemoteAddr { get; } 194 | 195 | /// 196 | /// 远程端口 197 | /// 198 | [FilterMember("remotePort")] 199 | int RemotePort { get; } 200 | 201 | /// 202 | /// The packet length 203 | /// 204 | [FilterMember("length")] 205 | int Length { get; } 206 | } 207 | 208 | /// 209 | /// 通过本地计算机的网络数据包 210 | /// 211 | public interface IForward 212 | { 213 | /// 214 | /// 网卡索引 215 | /// 216 | [FilterMember("ifIdx")] 217 | int IfIdx { get; } 218 | 219 | /// 220 | /// 子网卡索引 221 | /// 222 | [FilterMember("subIfIdx")] 223 | int SubIfIdx { get; } 224 | 225 | /// 226 | /// 冒名顶替数据包 227 | /// 228 | [FilterMember("impostor")] 229 | bool Impostor { get; } 230 | 231 | /// 232 | /// ip分片包 233 | /// 234 | [FilterMember("fragment")] 235 | bool Fragment { get; } 236 | 237 | /// 238 | /// The packet length 239 | /// 240 | [FilterMember("length")] 241 | int Length { get; } 242 | } 243 | 244 | /// 245 | /// 网络流已建立/已删除事件 246 | /// 247 | public interface IFlow 248 | { 249 | /// 250 | /// 是否为出口 251 | /// 252 | [FilterMember("outbound")] 253 | bool Outbound { get; } 254 | 255 | /// 256 | /// 是否为入口 257 | /// 258 | [FilterMember("inbound")] 259 | bool Inbound { get; } 260 | 261 | /// 262 | /// 环回数据包 263 | /// 264 | [FilterMember("loopback")] 265 | bool Loopback { get; } 266 | 267 | /// 268 | /// 流的终结点ID 269 | /// 270 | [FilterMember("endpointId")] 271 | int EndpointId { get; } 272 | 273 | /// 274 | /// 流的父终结点ID 275 | /// 276 | [FilterMember("parentEndpointId")] 277 | int ParentEndpointId { get; } 278 | 279 | /// 280 | /// 与流关联的进程的ID 281 | /// 282 | [FilterMember("processId")] 283 | int ProcessId { get; } 284 | 285 | /// 286 | /// 本机地址 287 | /// 288 | [FilterMember("localAddr")] 289 | string LocalAddr { get; } 290 | 291 | /// 292 | /// 本机端口 293 | /// 294 | [FilterMember("localPort")] 295 | int LocalPort { get; } 296 | 297 | /// 298 | /// 远程地址 299 | /// 300 | [FilterMember("remoteAddr")] 301 | string RemoteAddr { get; } 302 | 303 | /// 304 | /// 远程端口 305 | /// 306 | [FilterMember("remotePort")] 307 | int RemotePort { get; } 308 | 309 | /// 310 | /// 协议类型(ProtocolType) 311 | /// 312 | [FilterMember("protocol")] 313 | ProtocolType Protocol { get; } 314 | } 315 | 316 | /// 317 | /// 套接字操作事件 318 | /// 319 | public interface ISocket 320 | { 321 | /// 322 | /// 环回数据包 323 | /// 324 | [FilterMember("loopback")] 325 | bool Loopback { get; } 326 | 327 | /// 328 | /// 套接字操作的终结点 ID 329 | /// 330 | [FilterMember("endpointId")] 331 | int EndpointId { get; } 332 | 333 | /// 334 | /// 套接字操作的父终结点 ID 335 | /// 336 | [FilterMember("parentEndpointId")] 337 | int ParentEndpointId { get; } 338 | 339 | /// 340 | /// 与套接字操作关联的进程的 ID 341 | /// 342 | [FilterMember("processId")] 343 | int ProcessId { get; } 344 | 345 | /// 346 | /// 本机地址 347 | /// 348 | [FilterMember("localAddr")] 349 | string LocalAddr { get; } 350 | 351 | /// 352 | /// 本机端口 353 | /// 354 | [FilterMember("localPort")] 355 | int LocalPort { get; } 356 | 357 | /// 358 | /// 远程地址 359 | /// 360 | [FilterMember("remoteAddr")] 361 | string RemoteAddr { get; } 362 | 363 | /// 364 | /// 远程端口 365 | /// 366 | [FilterMember("remotePort")] 367 | int RemotePort { get; } 368 | 369 | /// 370 | /// 协议类型(ProtocolType) 371 | /// 372 | [FilterMember("protocol")] 373 | ProtocolType Protocol { get; } 374 | } 375 | 376 | /// 377 | /// WinDivert处理事件 378 | /// 379 | public interface IReflect 380 | { 381 | /// 382 | /// WinDivert打开句柄的进程的ID 383 | /// 384 | [FilterMember("processId")] 385 | int ProcessId { get; } 386 | 387 | /// 388 | /// WinDivertLayer 389 | /// 390 | [FilterMember("layer")] 391 | string Layer { get; } 392 | /// 393 | /// 优先级 394 | /// 395 | [FilterMember("priority")] 396 | int Priority { get; } 397 | } 398 | 399 | /// 400 | /// IPV4对象 401 | /// 402 | public interface IIP 403 | { 404 | /// 405 | /// 获取或设置Internet Header Length 406 | /// ipv4头总字节为该值的4倍 407 | /// 408 | [FilterMember] 409 | int HdrLength { get; } 410 | 411 | /// 412 | /// 版本 413 | /// 414 | [FilterMember] 415 | int Version { get; } 416 | 417 | /// 418 | /// 服务类型 419 | /// 420 | [FilterMember] 421 | int TOS { get; } 422 | 423 | /// 424 | /// 包长度 425 | /// 426 | [FilterMember] 427 | int Length { get; } 428 | 429 | /// 430 | /// 报文的id 431 | /// 432 | [FilterMember] 433 | int Id { get; } 434 | 435 | /// 436 | /// 生存时间 437 | /// 438 | [FilterMember] 439 | int TTL { get; } 440 | 441 | /// 442 | /// 协议 443 | /// ProtocolType 444 | /// 445 | [FilterMember] 446 | ProtocolType Protocol { get; } 447 | 448 | /// 449 | /// 检验和 450 | /// 451 | [FilterMember] 452 | int Checksum { get; } 453 | 454 | /// 455 | /// 源IP地址 456 | /// 457 | [FilterMember] 458 | string SrcAddr { get; } 459 | 460 | /// 461 | /// 远程IP地址 462 | /// 463 | [FilterMember] 464 | string DstAddr { get; } 465 | } 466 | 467 | /// 468 | /// ipv6 469 | /// 470 | public interface IIPV6 471 | { 472 | /// 473 | /// 版本 474 | /// 475 | [FilterMember] 476 | int Version { get; } 477 | 478 | /// 479 | /// 有效负载长度 480 | /// 481 | [FilterMember] 482 | int Length { get; } 483 | 484 | /// 485 | /// 下一个报头 486 | /// 487 | [FilterMember] 488 | int NextHdr { get; } 489 | 490 | /// 491 | /// 跳跃限制 492 | /// 493 | [FilterMember] 494 | int HopLimit { get; } 495 | 496 | /// 497 | /// 源IP地址 498 | /// 499 | [FilterMember] 500 | string SrcAddr { get; } 501 | 502 | /// 503 | /// 目的IP地址 504 | /// 505 | [FilterMember] 506 | string DstAddr { get; } 507 | } 508 | 509 | /// 510 | /// 传输层 511 | /// 512 | public interface ITransfer 513 | { 514 | /// 515 | /// 校验和 516 | /// 517 | [FilterMember] 518 | int Checksum { get; } 519 | 520 | /// 521 | /// 源端口号 522 | /// 523 | [FilterMember] 524 | int SrcPort { get; } 525 | 526 | /// 527 | /// 目的端口号 528 | /// 529 | [FilterMember] 530 | int DstPort { get; } 531 | 532 | /// 533 | /// 负载数据长度 534 | /// 535 | [FilterMember] 536 | int PayloadLength { get; } 537 | 538 | /// 539 | /// 8位形式的负载数据 540 | /// 使用索引来读取 541 | /// 542 | [FilterMember] 543 | byte[] Payload { get; } 544 | 545 | /// 546 | /// 16位形式的负载数据 547 | /// 使用索引来读取 548 | /// 549 | [FilterMember] 550 | ushort[] Payload16 { get; } 551 | 552 | /// 553 | /// 32位形式的负载数据 554 | /// 使用索引来读取 555 | /// 556 | [FilterMember] 557 | uint[] Payload32 { get; } 558 | } 559 | 560 | /// 561 | /// tcp 562 | /// 563 | public interface ITcp : ITransfer 564 | { 565 | /// 566 | /// 序列码 567 | /// 568 | [FilterMember] 569 | int SeqNum { get; } 570 | 571 | /// 572 | /// 确认码 573 | /// 574 | [FilterMember] 575 | int AckNum { get; } 576 | 577 | /// 578 | /// 结束位 579 | /// 580 | [FilterMember] 581 | bool Fin { get; } 582 | 583 | /// 584 | /// 请求位 585 | /// 586 | [FilterMember] 587 | bool Syn { get; } 588 | 589 | /// 590 | /// 重置位 591 | /// 592 | [FilterMember] 593 | bool Rst { get; } 594 | 595 | /// 596 | /// 确认位 597 | /// 598 | [FilterMember] 599 | bool Ack { get; } 600 | 601 | /// 602 | /// 推送位 603 | /// 604 | [FilterMember] 605 | bool Psh { get; } 606 | 607 | /// 608 | /// 紧急位 609 | /// 610 | [FilterMember] 611 | bool Urg { get; } 612 | 613 | /// 614 | /// 滑动窗口 615 | /// 616 | [FilterMember] 617 | int Window { get; } 618 | 619 | /// 620 | /// 紧急指针 621 | /// 622 | [FilterMember] 623 | int UrgPtr { get; } 624 | } 625 | 626 | /// 627 | /// udp 628 | /// 629 | public interface IUdp : ITransfer 630 | { 631 | /// 632 | /// 获取或设置Udp包长度 633 | /// 含头部的8字节 634 | /// 635 | [FilterMember] 636 | int Length { get; } 637 | } 638 | 639 | /// 640 | /// ICMP 641 | /// 642 | public interface IICmp 643 | { 644 | /// 645 | /// 类型 646 | /// 647 | [FilterMember] 648 | IcmpV4MessageType Type { get; } 649 | 650 | /// 651 | /// 代码 652 | /// 653 | [FilterMember] 654 | IcmpV4UnreachableCode Code { get; } 655 | 656 | /// 657 | /// 检验和 658 | /// 659 | [FilterMember] 660 | int Checksum { get; } 661 | 662 | /// 663 | /// Rest of header 664 | /// 665 | [FilterMember] 666 | int Body { get; } 667 | } 668 | 669 | /// 670 | /// ICMP 671 | /// 672 | public interface IICmpV6 673 | { 674 | /// 675 | /// 类型 676 | /// 677 | [FilterMember] 678 | IcmpV6MessageType Type { get; } 679 | 680 | /// 681 | /// 代码 682 | /// 683 | [FilterMember] 684 | IcmpV6UnreachableCode Code { get; } 685 | 686 | /// 687 | /// 检验和 688 | /// 689 | [FilterMember] 690 | int Checksum { get; } 691 | 692 | /// 693 | /// Rest of header 694 | /// 695 | [FilterMember] 696 | int Body { get; } 697 | } 698 | 699 | 700 | /// 701 | /// Filter成员特性 702 | /// 703 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 704 | public sealed class FilterMemberAttribute : Attribute 705 | { 706 | /// 707 | /// 成员名称 708 | /// 709 | public string? Name { get; } 710 | 711 | /// 712 | /// Filter成员特性 713 | /// 714 | public FilterMemberAttribute() 715 | { 716 | } 717 | 718 | /// 719 | /// Filter成员特性 720 | /// 721 | /// 成员名称 722 | public FilterMemberAttribute(string name) 723 | { 724 | this.Name = name; 725 | } 726 | } 727 | } 728 | } 729 | -------------------------------------------------------------------------------- /WindivertDotnet/IPV4Header.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Diagnostics; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace WindivertDotnet 9 | { 10 | /// 11 | /// IPV4头 12 | /// 13 | [DebuggerDisplay("SrcAddr = {SrcAddr}, DstAddr = {DstAddr}, Size = {HdrLength * 4}")] 14 | [StructLayout(LayoutKind.Explicit)] 15 | public struct IPV4Header 16 | { 17 | private const int IPV4_SIZE = sizeof(int); 18 | 19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 20 | [FieldOffset(0)] 21 | private byte bitfield; 22 | 23 | /// 24 | /// 获取或设置Internet Header Length 25 | /// ipv4头总字节为该值的4倍 26 | /// 27 | public byte HdrLength 28 | { 29 | get => (byte)(bitfield & 0xFu); 30 | set => bitfield = (byte)((bitfield & ~0xFu) | (value & 0xFu)); 31 | } 32 | 33 | /// 34 | /// 获取或设置版本 35 | /// 36 | public IPVersion Version 37 | { 38 | get => (IPVersion)((bitfield >> 4) & 0xFu); 39 | set => bitfield = (byte)((bitfield & ~(0xFu << 4)) | (((byte)value & 0xFu) << 4)); 40 | } 41 | 42 | /// 43 | /// 获取或设置服务类型 44 | /// 45 | [FieldOffset(1)] 46 | public byte TOS; 47 | 48 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 49 | [FieldOffset(2)] 50 | private ushort length; 51 | 52 | /// 53 | /// 获取或设置IP数据包的长度 54 | /// 包含IPv4头的长度 55 | /// 56 | public ushort Length 57 | { 58 | get => BinaryPrimitives.ReverseEndianness(length); 59 | set => length = BinaryPrimitives.ReverseEndianness(value); 60 | } 61 | 62 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 63 | [FieldOffset(4)] 64 | private ushort id; 65 | 66 | /// 67 | /// 获取或设置报文的id 68 | /// 其初始值由系统随机生成;每发送一个数据报,其值就加1 69 | /// 该值在数据报分片时被复制到每个分片中,因此同一个数据报的所有分片都具有相同的标识值 70 | /// 71 | public ushort Id 72 | { 73 | get => BinaryPrimitives.ReverseEndianness(id); 74 | set => id = BinaryPrimitives.ReverseEndianness(value); 75 | } 76 | 77 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 78 | [FieldOffset(6)] 79 | private byte frag; 80 | 81 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 82 | [FieldOffset(6)] 83 | private ushort fragOff0; 84 | 85 | /// 86 | /// 获取或设置分片偏移 87 | /// 88 | public ushort FragOff0 89 | { 90 | get => BinaryPrimitives.ReverseEndianness(fragOff0); 91 | set => fragOff0 = BinaryPrimitives.ReverseEndianness(value); 92 | } 93 | 94 | /// 95 | /// 标记位 96 | /// 97 | public FragmentFlag FragmentFlags 98 | { 99 | get => (FragmentFlag)(this.frag >> 5); 100 | set => this.frag = (byte)(((uint)((byte)value << 5)) | (this.frag & 0b_0001_1111u)); 101 | } 102 | 103 | /// 104 | /// 分片偏移量13位 105 | /// 106 | public ushort FragmentOffset 107 | { 108 | get => (ushort)(this.FragOff0 & 0b_0001_1111_1111_1111u); 109 | set => this.FragOff0 = (ushort)((value & 0b_0001_1111_1111_1111u) | (this.FragOff0 & 0b_1110_0000_0000_0000u)); 110 | } 111 | 112 | /// 113 | /// 获取或设置生存时间 114 | /// 115 | [FieldOffset(8)] 116 | public byte TTL; 117 | 118 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 119 | [FieldOffset(9)] 120 | private byte protocol; 121 | 122 | 123 | /// 124 | /// 获取或设置协议类型 125 | /// 126 | public ProtocolType Protocol 127 | { 128 | get => (ProtocolType)protocol; 129 | set => protocol = (byte)value; 130 | } 131 | 132 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 133 | [FieldOffset(10)] 134 | private ushort checksum; 135 | 136 | /// 137 | /// 获取或设置检验和 138 | /// 139 | public ushort Checksum 140 | { 141 | get => BinaryPrimitives.ReverseEndianness(checksum); 142 | set => checksum = BinaryPrimitives.ReverseEndianness(value); 143 | } 144 | 145 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 146 | [FieldOffset(12)] 147 | private unsafe fixed byte srcAddr[IPV4_SIZE]; 148 | 149 | /// 150 | /// 获取或设置源IPv4 151 | /// 152 | public unsafe IPAddress SrcAddr 153 | { 154 | get 155 | { 156 | fixed (void* ptr = this.srcAddr) 157 | { 158 | return GetIPAddress(ptr); 159 | } 160 | } 161 | set 162 | { 163 | fixed (void* ptr = this.srcAddr) 164 | { 165 | SetIPAddress(ptr, value); 166 | } 167 | } 168 | } 169 | 170 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 171 | [FieldOffset(16)] 172 | private unsafe fixed byte dstAddr[IPV4_SIZE]; 173 | 174 | /// 175 | /// 获取或设置目的IPv4 176 | /// 177 | public unsafe IPAddress DstAddr 178 | { 179 | get 180 | { 181 | fixed (void* ptr = this.dstAddr) 182 | { 183 | return GetIPAddress(ptr); 184 | } 185 | } 186 | set 187 | { 188 | fixed (void* ptr = this.dstAddr) 189 | { 190 | SetIPAddress(ptr, value); 191 | } 192 | } 193 | } 194 | 195 | private unsafe static IPAddress GetIPAddress(void* ptr) 196 | { 197 | var span = new Span(ptr, IPV4_SIZE); 198 | return new IPv4Address(span); 199 | } 200 | 201 | private unsafe static void SetIPAddress(void* ptr, IPAddress value) 202 | { 203 | if (value.AddressFamily != AddressFamily.InterNetwork) 204 | { 205 | throw new ArgumentException($"AddressFamily要求必须为{AddressFamily.InterNetwork}", nameof(value)); 206 | } 207 | 208 | var span = new Span(ptr, IPV4_SIZE); 209 | value.TryWriteBytes(span, out _); 210 | } 211 | 212 | private class IPv4Address : IPAddress 213 | { 214 | public IPv4Address(ReadOnlySpan span) 215 | : base(span) 216 | { 217 | } 218 | public override string ToString() 219 | { 220 | return base.ToString(); 221 | } 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /WindivertDotnet/IPV6Header.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Diagnostics; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | 7 | namespace WindivertDotnet 8 | { 9 | /// 10 | /// IPv6头 11 | /// 12 | [DebuggerDisplay("SrcAddr = {SrcAddr}, DstAddr = {DstAddr}, Size = {sizeof(IPV6Header)}")] 13 | public struct IPV6Header 14 | { 15 | private const int IPV6_SIZE = sizeof(int) * 4; 16 | 17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 18 | private byte bitfield1; 19 | 20 | /// 21 | /// 获取或设置流量类 22 | /// 23 | public byte TrafficClass0 24 | { 25 | get => (byte)(bitfield1 & 0xFu); 26 | set => bitfield1 = (byte)((bitfield1 & ~0xFu) | (value & 0xFu)); 27 | } 28 | 29 | /// 30 | /// 获取或设置版本 31 | /// 32 | public IPVersion Version 33 | { 34 | get => (IPVersion)((bitfield1 >> 4) & 0xFu); 35 | set => bitfield1 = (byte)((bitfield1 & ~(0xFu << 4)) | (((byte)value & 0xFu) << 4)); 36 | } 37 | 38 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 39 | private byte bitfield2; 40 | 41 | /// 42 | /// 获取或设置流标签 43 | /// 44 | public byte FlowLabel0 45 | { 46 | get => (byte)(bitfield2 & 0xFu); 47 | set => bitfield2 = (byte)((bitfield2 & ~0xFu) | (value & 0xFu)); 48 | } 49 | 50 | 51 | /// 52 | /// 获取或设置流量类 53 | /// 54 | public byte TrafficClass1 55 | { 56 | get => (byte)((bitfield2 >> 4) & 0xFu); 57 | set => bitfield2 = (byte)((bitfield2 & ~(0xFu << 4)) | ((value & 0xFu) << 4)); 58 | } 59 | 60 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 61 | private ushort flowLabel1; 62 | 63 | /// 64 | /// 获取或设置流标签 65 | /// 66 | public ushort FlowLabel1 67 | { 68 | get => BinaryPrimitives.ReverseEndianness(flowLabel1); 69 | set => flowLabel1 = BinaryPrimitives.ReverseEndianness(value); 70 | } 71 | 72 | 73 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 74 | private ushort length; 75 | 76 | /// 77 | /// 获取或设置有效负载长度 78 | /// 不包含IPV6头的固定40字节 79 | /// 80 | public ushort Length 81 | { 82 | get => BinaryPrimitives.ReverseEndianness(length); 83 | set => length = BinaryPrimitives.ReverseEndianness(value); 84 | } 85 | 86 | /// 87 | /// 获取或设置下一个报头 88 | /// 89 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 90 | private byte nextHdr; 91 | 92 | /// 93 | /// 获取或设置下一个报头协议 94 | /// 95 | public ProtocolType NextHdr 96 | { 97 | get => (ProtocolType)nextHdr; 98 | set => nextHdr = (byte)value; 99 | } 100 | 101 | /// 102 | /// 获取或设置跳跃限制 103 | /// 104 | public byte HopLimit; 105 | 106 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 107 | private unsafe fixed byte srcAddr[IPV6_SIZE]; 108 | 109 | /// 110 | /// 获取或设置源IPv6 111 | /// 112 | public unsafe IPAddress SrcAddr 113 | { 114 | get 115 | { 116 | fixed (void* ptr = this.srcAddr) 117 | { 118 | return GetIPAddress(ptr); 119 | } 120 | } 121 | set 122 | { 123 | fixed (void* ptr = this.srcAddr) 124 | { 125 | SetIPAddress(ptr, value); 126 | } 127 | } 128 | } 129 | 130 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 131 | private unsafe fixed byte dstAddr[IPV6_SIZE]; 132 | 133 | /// 134 | /// 获取或设置目的IPV6 135 | /// 136 | public unsafe IPAddress DstAddr 137 | { 138 | get 139 | { 140 | fixed (void* ptr = this.dstAddr) 141 | { 142 | return GetIPAddress(ptr); 143 | } 144 | } 145 | set 146 | { 147 | fixed (void* ptr = this.dstAddr) 148 | { 149 | SetIPAddress(ptr, value); 150 | } 151 | } 152 | } 153 | 154 | private unsafe static IPAddress GetIPAddress(void* ptr) 155 | { 156 | var span = new Span(ptr, IPV6_SIZE); 157 | return new IPv6Address(span); 158 | } 159 | 160 | private unsafe static void SetIPAddress(void* ptr, IPAddress value) 161 | { 162 | if (value.AddressFamily != AddressFamily.InterNetworkV6) 163 | { 164 | throw new ArgumentException($"AddressFamily要求必须为{AddressFamily.InterNetworkV6}", nameof(value)); 165 | } 166 | 167 | var span = new Span(ptr, IPV6_SIZE); 168 | value.TryWriteBytes(span, out _); 169 | } 170 | 171 | private class IPv6Address : IPAddress 172 | { 173 | public IPv6Address(ReadOnlySpan span) 174 | : base(span) 175 | { 176 | } 177 | public override string ToString() 178 | { 179 | return base.ToString(); 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /WindivertDotnet/IPVersion.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// ip版本 5 | /// 6 | public enum IPVersion : byte 7 | { 8 | /// 9 | /// v4 10 | /// 11 | V4 = 4, 12 | 13 | /// 14 | /// v6 15 | /// 16 | V6 = 6 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV4Header.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace WindivertDotnet 6 | { 7 | /// 8 | /// IcmpV4头结构 9 | /// 10 | [DebuggerDisplay("Type = {Type}, Code = {Code}, Size = {sizeof(IcmpV4Header)}")] 11 | [StructLayout(LayoutKind.Explicit)] 12 | public struct IcmpV4Header 13 | { 14 | /// 15 | /// 获取或设置类型 16 | /// 17 | [FieldOffset(0)] 18 | public IcmpV4MessageType Type; 19 | 20 | /// 21 | /// 获取或设置代码 22 | /// 23 | [FieldOffset(1)] 24 | public IcmpV4UnreachableCode Code; 25 | 26 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 27 | [FieldOffset(2)] 28 | private ushort checksum; 29 | 30 | /// 31 | /// 获取或设置检验和 32 | /// 33 | public ushort Checksum 34 | { 35 | get => BinaryPrimitives.ReverseEndianness(checksum); 36 | set => checksum = BinaryPrimitives.ReverseEndianness(value); 37 | } 38 | 39 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 40 | [FieldOffset(4)] 41 | private uint body; 42 | 43 | /// 44 | /// 获取或设置Rest of header 45 | /// 内容因 ICMP 类型和代码而异 46 | /// 47 | public uint Body 48 | { 49 | get => BinaryPrimitives.ReverseEndianness(body); 50 | set => body = BinaryPrimitives.ReverseEndianness(value); 51 | } 52 | 53 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 54 | [FieldOffset(4)] 55 | private ushort identifier; 56 | 57 | /// 58 | /// 获取或设置id 59 | /// 60 | public ushort Identifier 61 | { 62 | get => BinaryPrimitives.ReverseEndianness(identifier); 63 | set => identifier = BinaryPrimitives.ReverseEndianness(value); 64 | } 65 | 66 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 67 | [FieldOffset(6)] 68 | private ushort sequenceNumber; 69 | 70 | /// 71 | /// 获取或设置序列号 72 | /// 73 | public ushort SequenceNumber 74 | { 75 | get => BinaryPrimitives.ReverseEndianness(sequenceNumber); 76 | set => sequenceNumber = BinaryPrimitives.ReverseEndianness(value); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV4MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// IcmpV4消息类型 5 | /// 6 | public enum IcmpV4MessageType : byte 7 | { 8 | #pragma warning disable 1591 9 | EchoReply = 0, 10 | DestinationUnreachable = 3, 11 | SourceQuench = 4, 12 | RedirectMessage = 5, 13 | EchoRequest = 8, 14 | RouterAdvertisement = 9, 15 | RouterSolicitation = 10, 16 | TimeExceeded = 11, 17 | ParameterProblemBadIPHeader = 12, 18 | Timestamp = 13, 19 | TimestampReply = 14, 20 | InformationRequest = 15, 21 | InformationReply = 16, 22 | AddressMaskRequest = 17, 23 | AddressMaskReply = 18, 24 | Traceroute = 30, 25 | #pragma warning restore 1591 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV4UnreachableCode.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// IcmpV4错误码 5 | /// 6 | public enum IcmpV4UnreachableCode : byte 7 | { 8 | #pragma warning disable 1591 9 | DestinationNetworkUnreachable = 0, 10 | DestinationHostUnreachable = 1, 11 | DestinationProtocolUnreachable = 2, 12 | DestinationPortUnreachable = 3, 13 | FragmentationRequiredAndDFFlagSet = 4, 14 | SourceRouteFailed = 5, 15 | DestinationNetworkUnknown = 6, 16 | DestinationHostUnknown = 7, 17 | SourceHostIsolated = 8, 18 | NetworkAdministrativelyProhibited = 9, 19 | HostAdministrativelyProhibited = 10, 20 | NetworkUnreachableForTos = 11, 21 | HostUnreachableForTos = 12, 22 | CommunicationAdministrativelyProhibited = 13, 23 | HostPrecedenceViolation = 14, 24 | PrecedenceCutoffInEffect = 15, 25 | #pragma warning restore 1591 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV6Header.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace WindivertDotnet 6 | { 7 | /// 8 | /// IcmpV6头结构 9 | /// 10 | [DebuggerDisplay("Type = {Type}, Code = {Code}, Size = {sizeof(IcmpV6Header)}")] 11 | [StructLayout(LayoutKind.Explicit)] 12 | public struct IcmpV6Header 13 | { 14 | /// 15 | /// 获取或设置类型 16 | /// 17 | [FieldOffset(0)] 18 | public IcmpV6MessageType Type; 19 | 20 | /// 21 | /// 获取或设置代码 22 | /// 23 | [FieldOffset(1)] 24 | public IcmpV6UnreachableCode Code; 25 | 26 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 27 | [FieldOffset(2)] 28 | private ushort checksum; 29 | 30 | /// 31 | /// 获取或设置检验和 32 | /// 33 | public ushort Checksum 34 | { 35 | get => BinaryPrimitives.ReverseEndianness(checksum); 36 | set => checksum = BinaryPrimitives.ReverseEndianness(value); 37 | } 38 | 39 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 40 | [FieldOffset(4)] 41 | private uint body; 42 | 43 | /// 44 | /// 获取或设置Rest of header 45 | /// 内容因 ICMP 类型和代码而异 46 | /// 47 | public uint Body 48 | { 49 | get => BinaryPrimitives.ReverseEndianness(body); 50 | set => body = BinaryPrimitives.ReverseEndianness(value); 51 | } 52 | 53 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 54 | [FieldOffset(4)] 55 | private ushort identifier; 56 | 57 | /// 58 | /// 获取或设置id 59 | /// 60 | public ushort Identifier 61 | { 62 | get => BinaryPrimitives.ReverseEndianness(identifier); 63 | set => identifier = BinaryPrimitives.ReverseEndianness(value); 64 | } 65 | 66 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 67 | [FieldOffset(6)] 68 | private ushort sequenceNumber; 69 | 70 | /// 71 | /// 获取或设置序列号 72 | /// 73 | public ushort SequenceNumber 74 | { 75 | get => BinaryPrimitives.ReverseEndianness(sequenceNumber); 76 | set => sequenceNumber = BinaryPrimitives.ReverseEndianness(value); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV6MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// IcmpV6消息类型 5 | /// 6 | public enum IcmpV6MessageType : byte 7 | { 8 | #pragma warning disable 1591 9 | DestinationUnreachable = 1, 10 | PacketTooBig = 2, 11 | TimeExceeded = 3, 12 | ParameterProblem = 4, 13 | EchoRequest = 128, 14 | EchoReply = 129, 15 | #pragma warning restore 1591 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WindivertDotnet/IcmpV6UnreachableCode.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// IcmpV6错误码 5 | /// 6 | public enum IcmpV6UnreachableCode : byte 7 | { 8 | #pragma warning disable 1591 9 | NoRouteToDestination = 0, 10 | CommunicationAdministrativelyProhibited = 1, 11 | BeyondScopeOfSourceAddress = 2, 12 | AddressUnreachable = 3, 13 | PortUnreachable = 4, 14 | SourceAddressFailedPolicy = 5, 15 | RejectRouteToDestination = 6, 16 | SourceRoutingHeaderError = 7, 17 | #pragma warning restore 1591 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /WindivertDotnet/IdSeqNum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace WindivertDotnet 5 | { 6 | /// 7 | /// id或序列号生成器 8 | /// 9 | public sealed class IdSeqNum 10 | { 11 | private static readonly Random random = new(); 12 | private int value = random.Next(); 13 | 14 | /// 15 | /// 获取公共实例 16 | /// 17 | public static IdSeqNum Shared { get; } = new IdSeqNum(); 18 | 19 | /// 20 | /// 生成下一个UInt16值 21 | /// 22 | /// 23 | public ushort NextUInt16() => (ushort)Interlocked.Increment(ref value); 24 | 25 | /// 26 | /// 生成下一个UInt32值 27 | /// 28 | /// 29 | public uint NextUInt32() => (uint)Interlocked.Increment(ref value); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/InteropServices/IPHelpApiNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using WindivertDotnet; 3 | 4 | namespace System.Runtime.InteropServices 5 | { 6 | [SupportedOSPlatform("windows")] 7 | unsafe static class IPHelpApiNative 8 | { 9 | private const string library = "iphlpapi.dll"; 10 | 11 | [DllImport(library)] 12 | public extern static int GetBestInterfaceEx( 13 | ref SockAddress dstSockAddr, 14 | out int index); 15 | 16 | [DllImport(library)] 17 | public static extern int GetBestRoute2( 18 | IntPtr interfaceLuid, 19 | int interfaceIndex, 20 | SockAddress* pSrcSocketAddr, 21 | ref SockAddress dstSocketAddr, 22 | uint addressSortOptions, 23 | byte* pBestRoute, // 103字节 24 | ref SockAddress bestSrcSocketAddr); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/InteropServices/Kernel32Native.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using System.Threading; 3 | 4 | namespace System.Runtime.InteropServices 5 | { 6 | [SupportedOSPlatform("windows")] 7 | static class Kernel32Native 8 | { 9 | private const string library = "kernel32.dll"; 10 | 11 | [DllImport(library, SetLastError = true, CharSet = CharSet.Ansi)] 12 | public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); 13 | 14 | 15 | [DllImport(library, SetLastError = true)] 16 | public unsafe static extern bool CancelIoEx(SafeHandle handle, NativeOverlapped* lpOverlapped); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/InteropServices/MemoryNative.cs: -------------------------------------------------------------------------------- 1 | namespace System.Runtime.InteropServices 2 | { 3 | /// 4 | /// 提供非托管内容的申请和释放 5 | /// 6 | static unsafe class MemoryNative 7 | { 8 | #if NET6_0_OR_GREATER 9 | public static IntPtr AllocZeroed(int size) 10 | { 11 | var pointer = NativeMemory.AllocZeroed((uint)size); 12 | return new IntPtr(pointer); 13 | } 14 | 15 | public static void Free(IntPtr handle) 16 | { 17 | NativeMemory.Free(handle.ToPointer()); 18 | } 19 | #else 20 | public static IntPtr AllocZeroed(int size) 21 | { 22 | var handle = Marshal.AllocHGlobal(size); 23 | new Span(handle.ToPointer(), size).Clear(); 24 | return handle; 25 | } 26 | 27 | public static void Free(IntPtr handle) 28 | { 29 | Marshal.FreeHGlobal(handle); 30 | } 31 | #endif 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/InteropServices/WinDivertNative.Loader.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/Runtime/InteropServices/WinDivertNative.Loader.cs -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/InteropServices/WinDivertNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using System.Threading; 3 | using WindivertDotnet; 4 | 5 | namespace System.Runtime.InteropServices 6 | { 7 | [SupportedOSPlatform("windows")] 8 | static unsafe partial class WinDivertNative 9 | { 10 | private const string library = "WinDivert.dll"; 11 | 12 | 13 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 14 | public static extern IntPtr WinDivertOpen( 15 | [MarshalAs(UnmanagedType.LPStr)] string filter, 16 | WinDivertLayer layer, 17 | short priority, 18 | WinDivertFlag flags); 19 | 20 | 21 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 22 | public static extern bool WinDivertClose( 23 | IntPtr handle); 24 | 25 | 26 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 27 | public static extern bool WinDivertRecvEx( 28 | WinDivert divert, 29 | WinDivertPacket packet, 30 | int packetLen, 31 | int* pRecvLen, 32 | ulong flags, 33 | WinDivertAddress addr, 34 | int* pAddrLen, 35 | NativeOverlapped* lpOverlapped); 36 | 37 | 38 | 39 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 40 | public static extern bool WinDivertSendEx( 41 | WinDivert divert, 42 | WinDivertPacket packet, 43 | int packetLen, 44 | int* pSendLen, 45 | ulong flags, 46 | WinDivertAddress addr, 47 | int addrLen, 48 | NativeOverlapped* lpOverlapped); 49 | 50 | 51 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 52 | public static extern bool WinDivertShutdown( 53 | WinDivert divert, 54 | WinDivertShutdown how); 55 | 56 | 57 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 58 | public static extern bool WinDivertSetParam( 59 | WinDivert divert, 60 | WinDivertParam param1, 61 | long value); 62 | 63 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 64 | public static extern bool WinDivertGetParam( 65 | WinDivert divert, 66 | WinDivertParam param1, 67 | ref long pValue); 68 | 69 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 70 | public static extern bool WinDivertHelperFormatFilter( 71 | [MarshalAs(UnmanagedType.LPStr)] string filter, 72 | WinDivertLayer layer, 73 | void* buffer, 74 | int bufLen); 75 | 76 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 77 | public static extern bool WinDivertHelperCompileFilter( 78 | [MarshalAs(UnmanagedType.LPStr)] string filter, 79 | WinDivertLayer layer, 80 | void* obj, 81 | int objLen, 82 | out IntPtr errorStr, 83 | out int errorPos); 84 | 85 | 86 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 87 | public static extern int WinDivertHelperHashPacket( 88 | WinDivertPacket packet, 89 | int packetLen, 90 | long seed); 91 | 92 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 93 | public static extern bool WinDivertHelperParsePacket( 94 | WinDivertPacket packet, 95 | int packetLen, 96 | IPV4Header** ppIpHdr, 97 | IPV6Header** ppIpv6Hdr, 98 | byte* pProtocol, 99 | IcmpV4Header** ppIcmpHdr, 100 | IcmpV6Header** ppIcmpv6Hdr, 101 | TcpHeader** ppTcpHdr, 102 | UdpHeader** ppUdpHdr, 103 | byte** ppData, 104 | int* pDataLen, 105 | byte** ppNext, 106 | int* pNextLen); 107 | 108 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 109 | public static extern bool WinDivertHelperCalcChecksums( 110 | WinDivertPacket packet, 111 | int packetLen, 112 | WinDivertAddress addr, 113 | ChecksumsFlag flags); 114 | 115 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 116 | public static extern bool WinDivertHelperDecrementTTL( 117 | WinDivertPacket packet, 118 | int packetLen); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /WindivertDotnet/Runtime/Versioning/SupportedOSPlatformAttribute.cs: -------------------------------------------------------------------------------- 1 | #if NETCOREAPP3_1 2 | namespace System.Runtime.Versioning 3 | { 4 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] 5 | sealed class SupportedOSPlatformAttribute : Attribute 6 | { 7 | public string PlatformName { get; } 8 | 9 | public SupportedOSPlatformAttribute(string platformName) 10 | { 11 | this.PlatformName = platformName; 12 | } 13 | } 14 | } 15 | #endif -------------------------------------------------------------------------------- /WindivertDotnet/SockAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | 6 | namespace WindivertDotnet 7 | { 8 | /// 9 | /// SockAddress结构体 10 | /// 11 | unsafe struct SockAddress 12 | { 13 | private const int V4_SIZE = 4; 14 | private const int V6_SIZE = 16; 15 | 16 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 17 | private ushort addressFamily; 18 | 19 | public ushort Port; 20 | 21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 22 | private fixed byte addressV4[V4_SIZE]; 23 | 24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 25 | private fixed byte addressV6[V6_SIZE]; 26 | 27 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 28 | private uint scopeId; 29 | 30 | /// 31 | /// 获取或设置IP 32 | /// 33 | /// 34 | public IPAddress IPAddress 35 | { 36 | get 37 | { 38 | var family = (AddressFamily)this.addressFamily; 39 | if (family == AddressFamily.InterNetwork) 40 | { 41 | fixed (void* ptr = this.addressV4) 42 | { 43 | return new IPAddress(new Span(ptr, V4_SIZE)); 44 | } 45 | } 46 | else if (family == AddressFamily.InterNetworkV6) 47 | { 48 | fixed (void* ptr = this.addressV6) 49 | { 50 | return new IPAddress(new Span(ptr, V6_SIZE), this.scopeId); 51 | } 52 | } 53 | throw new NotSupportedException($"不支持的AddressFamily: {family}"); 54 | } 55 | set 56 | { 57 | this.addressFamily = (ushort)value.AddressFamily; 58 | if (value.AddressFamily == AddressFamily.InterNetwork) 59 | { 60 | fixed (void* ptr = this.addressV4) 61 | { 62 | var span = new Span(ptr, V4_SIZE); 63 | value.TryWriteBytes(span, out _); 64 | } 65 | } 66 | else if (value.AddressFamily == AddressFamily.InterNetworkV6) 67 | { 68 | fixed (void* ptr = this.addressV6) 69 | { 70 | var span = new Span(ptr, V6_SIZE); 71 | value.TryWriteBytes(span, out _); 72 | this.scopeId = (uint)value.ScopeId; 73 | } 74 | } 75 | else 76 | { 77 | throw new NotSupportedException($"不支持AddressFamily: {value.AddressFamily}"); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /WindivertDotnet/TcpFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// tcp标记位 7 | /// 8 | [Flags] 9 | public enum TcpFlag : byte 10 | { 11 | /// 12 | /// 结束位 13 | /// 14 | Fin = 0x1, 15 | 16 | /// 17 | /// 请求位 18 | /// 19 | Syn = 0x2, 20 | 21 | /// 22 | /// 重置位 23 | /// 24 | Rst = 0x4, 25 | 26 | /// 27 | /// 推送位 28 | /// 29 | Psh = 0x8, 30 | 31 | /// 32 | /// 确认位 33 | /// 34 | Ack = 0x10, 35 | 36 | /// 37 | /// 紧急位 38 | /// 39 | Urg = 0x20, 40 | 41 | /// 42 | /// 保留1 43 | /// 44 | Reserved1 = 0x40, 45 | 46 | /// 47 | /// 保留2 48 | /// 49 | Reserved2 = 0x80, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /WindivertDotnet/TcpHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.Diagnostics; 3 | 4 | namespace WindivertDotnet 5 | { 6 | /// 7 | /// Tcp头 8 | /// 9 | [DebuggerDisplay("SrcPort = {SrcPort}, DstPort = {DstPort}, Flags = {Flags}, Size = {HdrLength * 4}")] 10 | public struct TcpHeader 11 | { 12 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 13 | private ushort srcPort; 14 | 15 | /// 16 | /// 获取或设置源端口 17 | /// 18 | public ushort SrcPort 19 | { 20 | get => BinaryPrimitives.ReverseEndianness(srcPort); 21 | set => srcPort = BinaryPrimitives.ReverseEndianness(value); 22 | } 23 | 24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 25 | private ushort dstPort; 26 | 27 | /// 28 | /// 获取或设置目的端口 29 | /// 30 | public ushort DstPort 31 | { 32 | get => BinaryPrimitives.ReverseEndianness(dstPort); 33 | set => dstPort = BinaryPrimitives.ReverseEndianness(value); 34 | } 35 | 36 | 37 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 38 | private uint seqNum; 39 | 40 | /// 41 | /// 获取或设置序列码 42 | /// 43 | public uint SeqNum 44 | { 45 | get => BinaryPrimitives.ReverseEndianness(seqNum); 46 | set => seqNum = BinaryPrimitives.ReverseEndianness(value); 47 | } 48 | 49 | 50 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 51 | private uint ackNum; 52 | 53 | /// 54 | /// 获取或设置确认码 55 | /// 56 | public uint AckNum 57 | { 58 | get => BinaryPrimitives.ReverseEndianness(ackNum); 59 | set => ackNum = BinaryPrimitives.ReverseEndianness(value); 60 | } 61 | 62 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 63 | private byte bitfield; 64 | 65 | 66 | /// 67 | /// 保留 68 | /// 69 | public byte Reserved1 70 | { 71 | get => (byte)(bitfield & 0xFu); 72 | set => bitfield = (byte)((bitfield & ~0xFu) | (value & 0xFu)); 73 | } 74 | 75 | /// 76 | /// 获取或设置Internet Header Length 77 | /// Tcp头总字节为该值的4倍 78 | /// 79 | public byte HdrLength 80 | { 81 | get => (byte)((bitfield >> 4) & 0xFu); 82 | set => bitfield = (byte)((bitfield & ~(0xFu << 4)) | ((value & 0xFu) << 4)); 83 | } 84 | 85 | /// 86 | /// 标记位 87 | /// 88 | public TcpFlag Flags; 89 | 90 | 91 | /// 92 | /// 获取或设置结束位 93 | /// 94 | public bool Fin 95 | { 96 | get => this.Flags.HasFlag(TcpFlag.Fin); 97 | set => this.SetFlag(TcpFlag.Fin, value); 98 | } 99 | 100 | /// 101 | /// 获取或设置请求位 102 | /// 103 | public bool Syn 104 | { 105 | get => this.Flags.HasFlag(TcpFlag.Syn); 106 | set => this.SetFlag(TcpFlag.Syn, value); 107 | } 108 | 109 | /// 110 | /// 获取或设置重置位 111 | /// 112 | public bool Rst 113 | { 114 | get => this.Flags.HasFlag(TcpFlag.Rst); 115 | set => this.SetFlag(TcpFlag.Rst, value); 116 | } 117 | 118 | /// 119 | /// 获取或设置推送位 120 | /// 121 | public bool Psh 122 | { 123 | get => this.Flags.HasFlag(TcpFlag.Psh); 124 | set => this.SetFlag(TcpFlag.Psh, value); 125 | } 126 | 127 | /// 128 | /// 获取或设置确认位 129 | /// 130 | public bool Ack 131 | { 132 | get => this.Flags.HasFlag(TcpFlag.Ack); 133 | set => this.SetFlag(TcpFlag.Ack, value); 134 | } 135 | 136 | /// 137 | /// 获取或设置紧急位 138 | /// 139 | public bool Urg 140 | { 141 | get => this.Flags.HasFlag(TcpFlag.Urg); 142 | set => this.SetFlag(TcpFlag.Urg, value); 143 | } 144 | 145 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 146 | private ushort window; 147 | 148 | /// 149 | /// 获取或设置滑动窗口 150 | /// 151 | public ushort Window 152 | { 153 | get => BinaryPrimitives.ReverseEndianness(window); 154 | set => window = BinaryPrimitives.ReverseEndianness(value); 155 | } 156 | 157 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 158 | private ushort checksum; 159 | 160 | /// 161 | /// 获取或设置校验和 162 | /// 163 | public ushort Checksum 164 | { 165 | get => BinaryPrimitives.ReverseEndianness(checksum); 166 | set => checksum = BinaryPrimitives.ReverseEndianness(value); 167 | } 168 | 169 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 170 | private ushort urgPtr; 171 | 172 | /// 173 | /// 获取或设置紧急指针 174 | /// 175 | public ushort UrgPtr 176 | { 177 | get => BinaryPrimitives.ReverseEndianness(urgPtr); 178 | set => urgPtr = BinaryPrimitives.ReverseEndianness(value); 179 | } 180 | 181 | private void SetFlag(TcpFlag flag, bool value) 182 | { 183 | if (value) 184 | { 185 | this.Flags |= flag; 186 | } 187 | else 188 | { 189 | this.Flags &= ~flag; 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /WindivertDotnet/UdpHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.Diagnostics; 3 | 4 | namespace WindivertDotnet 5 | { 6 | /// 7 | /// Udp头 8 | /// 9 | [DebuggerDisplay("SrcPort = {SrcPort}, DstPort = {DstPort}, Size = {sizeof(UdpHeader)}")] 10 | public struct UdpHeader 11 | { 12 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 13 | private ushort srcPort; 14 | 15 | /// 16 | /// 获取或设置源端口 17 | /// 18 | public ushort SrcPort 19 | { 20 | get => BinaryPrimitives.ReverseEndianness(srcPort); 21 | set => srcPort = BinaryPrimitives.ReverseEndianness(value); 22 | } 23 | 24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 25 | private ushort dstPort; 26 | 27 | /// 28 | /// 获取或设置目的端口 29 | /// 30 | public ushort DstPort 31 | { 32 | get => BinaryPrimitives.ReverseEndianness(dstPort); 33 | set => dstPort = BinaryPrimitives.ReverseEndianness(value); 34 | } 35 | 36 | 37 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 38 | private ushort length; 39 | 40 | /// 41 | /// 获取或设置Udp包长度 42 | /// [含头部的8字节] 43 | /// 44 | public ushort Length 45 | { 46 | get => BinaryPrimitives.ReverseEndianness(length); 47 | set => length = BinaryPrimitives.ReverseEndianness(value); 48 | } 49 | 50 | 51 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 52 | private ushort checksum; 53 | 54 | 55 | /// 56 | /// 获取或设置校验和 57 | /// 58 | public ushort Checksum 59 | { 60 | get => BinaryPrimitives.ReverseEndianness(checksum); 61 | set => checksum = BinaryPrimitives.ReverseEndianness(value); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivert.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.Runtime.InteropServices; 6 | using System.Runtime.Versioning; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace WindivertDotnet 11 | { 12 | /// 13 | /// 表示WinDivert的操作对象 14 | /// 15 | [SupportedOSPlatform("windows")] 16 | [DebuggerDisplay("Filter = {Filter}, Layer = {Layer}")] 17 | public class WinDivert : SafeHandleZeroOrMinusOneIsInvalid 18 | { 19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 20 | private readonly Lazy boundHandleLazy; 21 | 22 | /// 23 | /// 获取过滤器 24 | /// 25 | public string Filter { get; } 26 | 27 | /// 28 | /// 获取工作层 29 | /// 30 | public WinDivertLayer Layer { get; } 31 | 32 | /// 33 | /// 获取优先级 34 | /// 35 | public short Priority { get; } 36 | 37 | /// 38 | /// 获取工作方式标志 39 | /// 40 | public WinDivertFlag Flags { get; } 41 | 42 | /// 43 | /// 获取软件版本 44 | /// 45 | public Version Version { get; } 46 | 47 | 48 | /// 49 | /// 获取或设置列队的容量大小 50 | /// 默认为4096,范围为[32,16384] 51 | /// 52 | /// 53 | /// 54 | public long QueueLength 55 | { 56 | get => this.GetParam(WinDivertParam.QueueLength); 57 | set => this.SetParam(WinDivertParam.QueueLength, value); 58 | } 59 | 60 | /// 61 | /// 获取或设自动丢弃数据包之前可以排队的最短时长 62 | /// 默认为2s,范围为100ms到16s 63 | /// 64 | /// 65 | /// 66 | public TimeSpan QueueTime 67 | { 68 | get => TimeSpan.FromMilliseconds(this.GetParam(WinDivertParam.QueueTime)); 69 | set => this.SetParam(WinDivertParam.QueueTime, (long)value.TotalMilliseconds); 70 | } 71 | 72 | /// 73 | /// 获取或设置存储在数据包队列中的最大字节数 74 | /// 默认4MB,范围为64KB到32MB 75 | /// 76 | /// 77 | /// 78 | public long QueueSize 79 | { 80 | get => this.GetParam(WinDivertParam.QueueSize); 81 | set => this.SetParam(WinDivertParam.QueueSize, value); 82 | } 83 | 84 | /// 85 | /// 创建一个WinDivert实例 86 | /// 87 | /// 过滤器 https://reqrypt.org/windivert-doc.html#filter_language 88 | /// 工作层 89 | /// 优先级,值越大优先级越高[-30000,30000] 90 | /// 标记 91 | /// 92 | public WinDivert(Filter filter, WinDivertLayer layer, short priority = 0, WinDivertFlag flags = WinDivertFlag.None) 93 | : this(filter.ToString(), layer, priority, flags) 94 | { 95 | } 96 | 97 | /// 98 | /// 创建一个WinDivert实例 99 | /// 100 | /// 过滤器 https://reqrypt.org/windivert-doc.html#filter_language 101 | /// 工作层 102 | /// 优先级,值越大优先级越高[-30000,30000] 103 | /// 标记 104 | /// 105 | /// 106 | /// 107 | public WinDivert(string filter, WinDivertLayer layer, short priority = 0, WinDivertFlag flags = WinDivertFlag.None) 108 | : base(ownsHandle: true) 109 | { 110 | if (layer != WinDivertLayer.Network && flags == WinDivertFlag.None) 111 | { 112 | flags = WinDivertFlag.Sniff | WinDivertFlag.RecvOnly; 113 | } 114 | 115 | var compileFilter = WindivertDotnet.Filter.Compile(filter, layer); 116 | 117 | if (Enum.IsDefined(typeof(WinDivertLayer), layer) == false) 118 | { 119 | throw new ArgumentOutOfRangeException(nameof(layer)); 120 | } 121 | 122 | if (priority < -30000 || priority > 30000) 123 | { 124 | throw new ArgumentOutOfRangeException(nameof(priority), "值范围为[-30000,30000]"); 125 | } 126 | 127 | this.handle = WinDivertNative.WinDivertOpen(compileFilter, layer, priority, flags); 128 | this.boundHandleLazy = new Lazy(() => ThreadPoolBoundHandle.BindHandle(this)); 129 | 130 | if (this.IsInvalid == true) 131 | { 132 | throw new Win32Exception(); 133 | } 134 | 135 | this.Filter = WindivertDotnet.Filter.Format(filter, layer); 136 | this.Layer = layer; 137 | this.Priority = priority; 138 | this.Flags = flags; 139 | this.Version = this.GetVersion(); 140 | } 141 | 142 | /// 143 | /// 获取版本信息 144 | /// 145 | /// 146 | private Version GetVersion() 147 | { 148 | var major = this.GetParam(WinDivertParam.VersionMajor); 149 | var minor = this.GetParam(WinDivertParam.VersionMinor); 150 | return new Version((int)major, (int)minor); 151 | } 152 | 153 | /// 154 | /// 获取线程池绑定句柄 155 | /// 156 | /// 157 | internal ThreadPoolBoundHandle GetThreadPoolBoundHandle() 158 | { 159 | return this.boundHandleLazy.Value; 160 | } 161 | 162 | /// 163 | /// 同步阻塞读取数据包 164 | /// 165 | /// 数据包 166 | /// 地址信息 167 | /// 168 | /// 169 | public int Recv(WinDivertPacket packet, WinDivertAddress addr) 170 | { 171 | return this.Recv(packet, addr, CancellationToken.None); 172 | } 173 | 174 | /// 175 | /// 同步阻塞读取数据包 176 | /// 177 | /// 数据包 178 | /// 地址信息 179 | /// 取消令牌 180 | /// 181 | /// 182 | /// 183 | public int Recv(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken) 184 | { 185 | return this.RecvAsync(packet, addr, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); 186 | } 187 | 188 | /// 189 | /// 异步IO读取数据包 190 | /// 191 | /// 数据包 192 | /// 地址信息 193 | /// 194 | /// 195 | public ValueTask RecvAsync(WinDivertPacket packet, WinDivertAddress addr) 196 | { 197 | return this.RecvAsync(packet, addr, CancellationToken.None); 198 | } 199 | 200 | /// 201 | /// 异步IO读取数据包 202 | /// 203 | /// 数据包 204 | /// 地址信息 205 | /// 取消令牌 206 | /// 207 | /// 208 | /// 209 | public async ValueTask RecvAsync(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken) 210 | { 211 | using var operation = new WinDivertRecvOperation(this, packet, addr); 212 | return await operation.IOControlAsync(cancellationToken); 213 | } 214 | 215 | /// 216 | /// 同步阻塞发送数据包 217 | /// 218 | /// 数据包 219 | /// 地址信息 220 | /// 221 | /// 222 | /// 223 | public int Send(WinDivertPacket packet, WinDivertAddress addr) 224 | { 225 | return this.Send(packet, addr, CancellationToken.None); 226 | } 227 | 228 | /// 229 | /// 同步阻塞发送数据包 230 | /// 231 | /// 数据包 232 | /// 地址信息 233 | /// 取消令牌 234 | /// 235 | /// 236 | /// 237 | /// 238 | public int Send(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken) 239 | { 240 | return this.SendAsync(packet, addr, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult(); 241 | } 242 | 243 | /// 244 | /// 异步IO发送数据包 245 | /// 246 | /// 数据包 247 | /// 地址信息 248 | /// 249 | /// 250 | /// 251 | public ValueTask SendAsync(WinDivertPacket packet, WinDivertAddress addr) 252 | { 253 | return this.SendAsync(packet, addr, CancellationToken.None); 254 | } 255 | 256 | /// 257 | /// 异步IO发送数据包 258 | /// 259 | /// 数据包 260 | /// 地址信息 261 | /// 取消令牌 262 | /// 263 | /// 264 | /// 265 | /// 266 | public async ValueTask SendAsync(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken) 267 | { 268 | if (packet.Length == 0) 269 | { 270 | throw new ArgumentOutOfRangeException(nameof(packet), $"{nameof(WinDivertPacket.Length)}不能为0"); 271 | } 272 | 273 | using var operation = new WinDivertSendOperation(this, packet, addr); 274 | return await operation.IOControlAsync(cancellationToken); 275 | } 276 | 277 | /// 278 | /// 获取指定的参数值 279 | /// 280 | /// 281 | /// 282 | /// 283 | /// 284 | private long GetParam(WinDivertParam param) 285 | { 286 | if (this.boundHandleLazy.IsValueCreated) 287 | { 288 | throw new InvalidOperationException(); 289 | } 290 | 291 | var value = 0L; 292 | var status = WinDivertNative.WinDivertGetParam(this, param, ref value); 293 | return status ? value : throw new Win32Exception(); 294 | } 295 | 296 | /// 297 | /// 设置指定的参数值 298 | /// 299 | /// 300 | /// 301 | /// 302 | /// 303 | private void SetParam(WinDivertParam param, long value) 304 | { 305 | if (this.boundHandleLazy.IsValueCreated) 306 | { 307 | throw new InvalidOperationException(); 308 | } 309 | 310 | if (param == WinDivertParam.QueueSize) 311 | { 312 | if (value < 65535L || value > 33554432L) 313 | { 314 | throw new ArgumentOutOfRangeException(nameof(value)); 315 | } 316 | } 317 | 318 | if (param == WinDivertParam.QueueTime) 319 | { 320 | if (value < 100L || value > 16000L) 321 | { 322 | throw new ArgumentOutOfRangeException(nameof(value)); 323 | } 324 | } 325 | 326 | if (param == WinDivertParam.QueueLength) 327 | { 328 | if (value < 32L || value > 16384L) 329 | { 330 | throw new ArgumentOutOfRangeException(nameof(value)); 331 | } 332 | } 333 | 334 | if (WinDivertNative.WinDivertSetParam(this, param, value) == false) 335 | { 336 | throw new Win32Exception(); 337 | } 338 | } 339 | 340 | /// 341 | /// 关闭 342 | /// 343 | /// 关闭方式 344 | /// 345 | public bool Shutdown(WinDivertShutdown how = WinDivertShutdown.Both) 346 | { 347 | return WinDivertNative.WinDivertShutdown(this, how); 348 | } 349 | 350 | /// 351 | /// 释放句柄 352 | /// 353 | /// 354 | protected override bool ReleaseHandle() 355 | { 356 | return WinDivertNative.WinDivertClose(this.handle); 357 | } 358 | 359 | /// 360 | /// 释放资源 361 | /// 362 | /// 363 | protected override void Dispose(bool disposing) 364 | { 365 | if (this.boundHandleLazy.IsValueCreated) 366 | { 367 | this.boundHandleLazy.Value.Dispose(); 368 | } 369 | base.Dispose(disposing); 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertAddress.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace WindivertDotnet 7 | { 8 | /// 9 | /// 表示WinDivert地址信息 10 | /// 11 | [DebuggerDisplay("Flags = {Flags}")] 12 | public unsafe class WinDivertAddress : SafeHandleZeroOrMinusOneIsInvalid, ICloneable 13 | { 14 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 15 | private AddressStruct* Pointer => (AddressStruct*)this.handle.ToPointer(); 16 | 17 | /// 18 | /// 获取WinDivertAddress结构大小 19 | /// 20 | public static int Size { get; } = sizeof(AddressStruct); 21 | 22 | /// 23 | /// 获取或设置发生的时间戳 24 | /// 25 | public long Timestamp 26 | { 27 | get => this.Pointer->Timestamp; 28 | set => this.Pointer->Timestamp = value; 29 | } 30 | 31 | /// 32 | /// 获取或设置所在层 33 | /// 34 | public WinDivertLayer Layer 35 | { 36 | get => this.Pointer->Layer; 37 | set => this.Pointer->Layer = value; 38 | } 39 | 40 | /// 41 | /// 获取或设置事件类型 42 | /// 43 | public WinDivertEvent Event 44 | { 45 | get => this.Pointer->Event; 46 | set => this.Pointer->Event = value; 47 | } 48 | 49 | /// 50 | /// 获取或设置标记 51 | /// 52 | public WinDivertAddressFlag Flags 53 | { 54 | get => this.Pointer->Flags; 55 | set => this.Pointer->Flags = value; 56 | } 57 | 58 | /// 59 | /// 获取或设置保留 60 | /// 61 | public byte Reserved 62 | { 63 | get => this.Pointer->Reserved; 64 | set => this.Pointer->Reserved = value; 65 | } 66 | 67 | /// 68 | /// 获取或设置保留 69 | /// 70 | public uint Reserved2 71 | { 72 | get => this.Pointer->Reserved2; 73 | set => this.Pointer->Reserved2 = value; 74 | } 75 | 76 | /// 77 | /// 获取网络信息的指针 78 | /// 79 | public WinDivertDataNetwork* Network => &this.Pointer->Network; 80 | 81 | /// 82 | /// 获取网络流信息的指针 83 | /// 84 | public WinDivertDataFlow* Flow => &this.Pointer->Flow; 85 | 86 | /// 87 | /// 获取套接字信息的指针 88 | /// 89 | public WinDivertDataSocket* Socket => &this.Pointer->Socket; 90 | 91 | /// 92 | /// 获取WinDivert信息的指针 93 | /// 94 | public WinDivertDataReflect* Reflect => &this.Pointer->Reflect; 95 | 96 | /// 97 | /// WinDivert地址信息 98 | /// 99 | public WinDivertAddress() 100 | : base(ownsHandle: true) 101 | { 102 | this.handle = MemoryNative.AllocZeroed(Size); 103 | } 104 | 105 | /// 106 | /// 释放资源 107 | /// 108 | /// 109 | protected override bool ReleaseHandle() 110 | { 111 | MemoryNative.Free(this.handle); 112 | return true; 113 | } 114 | 115 | /// 116 | /// 清除数据 117 | /// 118 | public void Clear() 119 | { 120 | new Span(this.handle.ToPointer(), Size).Clear(); 121 | } 122 | 123 | /// 124 | /// 克隆 125 | /// 126 | /// 127 | public WinDivertAddress Clone() 128 | { 129 | var addr = new WinDivertAddress(); 130 | *addr.Pointer = *this.Pointer; 131 | return addr; 132 | } 133 | 134 | object ICloneable.Clone() 135 | { 136 | return this.Clone(); 137 | } 138 | 139 | [StructLayout(LayoutKind.Explicit)] 140 | private struct AddressStruct 141 | { 142 | [FieldOffset(0)] 143 | public long Timestamp; 144 | 145 | [FieldOffset(8)] 146 | public WinDivertLayer Layer; 147 | 148 | [FieldOffset(9)] 149 | public WinDivertEvent Event; 150 | 151 | [FieldOffset(10)] 152 | public WinDivertAddressFlag Flags; 153 | 154 | [FieldOffset(11)] 155 | public byte Reserved; 156 | 157 | [FieldOffset(12)] 158 | public uint Reserved2; 159 | 160 | [FieldOffset(16)] 161 | public WinDivertDataNetwork Network; 162 | 163 | [FieldOffset(16)] 164 | public WinDivertDataFlow Flow; 165 | 166 | [FieldOffset(16)] 167 | public WinDivertDataSocket Socket; 168 | 169 | [FieldOffset(16)] 170 | public WinDivertDataReflect Reflect; 171 | 172 | [FieldOffset(16)] 173 | public fixed byte Reserved3[64]; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertAddressFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// WinDivertAddress标记 7 | /// 8 | [Flags] 9 | public enum WinDivertAddressFlag : byte 10 | { 11 | /// 12 | /// 无 13 | /// 14 | None = 0, 15 | 16 | /// 17 | /// 嗅探事件模式 18 | /// 19 | Sniffed = 0x1, 20 | 21 | /// 22 | /// 出站数据包 23 | /// 24 | Outbound = 0x2, 25 | 26 | /// 27 | /// 环回数据包 28 | /// 29 | Loopback = 0x4, 30 | 31 | /// 32 | /// 冒名顶替数据包 33 | /// 34 | Impostor = 0x8, 35 | 36 | /// 37 | /// IPv6 数据包 38 | /// 39 | IPv6 = 0x10, 40 | 41 | /// 42 | /// IPv4 校验和有效 43 | /// 44 | IPChecksum = 0x20, 45 | 46 | /// 47 | /// tcp 校验和有效 48 | /// 49 | TCPChecksum = 0x40, 50 | 51 | /// 52 | /// udp 校验和有效 53 | /// 54 | UDPChecksum = 0x80 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertDataFlow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | 6 | namespace WindivertDotnet 7 | { 8 | /// 9 | /// 网络流信息 10 | /// 11 | public unsafe struct WinDivertDataFlow 12 | { 13 | private const int IPV6_SIZE = sizeof(int) * 4; 14 | 15 | /// 16 | /// 流的终结点ID 17 | /// 18 | public long EndpointId; 19 | 20 | /// 21 | /// 流的父终结点ID 22 | /// 23 | public long ParentEndpointId; 24 | 25 | /// 26 | /// 与流关联的进程的ID 27 | /// 28 | public int ProcessId; 29 | 30 | /// 31 | /// 本机地址 32 | /// 33 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 34 | private fixed byte localAddr[IPV6_SIZE]; 35 | 36 | /// 37 | /// 本机地址 38 | /// 39 | public IPAddress LocalAddr 40 | { 41 | get 42 | { 43 | fixed (void* ptr = this.localAddr) 44 | { 45 | return GetIPAddress(ptr); 46 | } 47 | } 48 | set 49 | { 50 | fixed (void* ptr = this.localAddr) 51 | { 52 | SetIPAddress(ptr, value); 53 | } 54 | } 55 | } 56 | 57 | /// 58 | /// 远程地址 59 | /// 60 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 61 | private fixed byte remoteAddr[IPV6_SIZE]; 62 | 63 | /// 64 | /// 远程地址 65 | /// 66 | public IPAddress RemoteAddr 67 | { 68 | get 69 | { 70 | fixed (void* ptr = this.remoteAddr) 71 | { 72 | return GetIPAddress(ptr); 73 | } 74 | } 75 | set 76 | { 77 | fixed (void* ptr = this.remoteAddr) 78 | { 79 | SetIPAddress(ptr, value); 80 | } 81 | } 82 | } 83 | 84 | /// 85 | /// 本机端口 86 | /// 87 | public ushort LocalPort; 88 | 89 | /// 90 | /// 远程端口 91 | /// 92 | public ushort RemotePort; 93 | 94 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 95 | private byte protocol; 96 | 97 | /// 98 | /// 协议类型 99 | /// 100 | public ProtocolType Protocol 101 | { 102 | get => (ProtocolType)protocol; 103 | set => protocol = (byte)value; 104 | } 105 | 106 | private static IPAddress GetIPAddress(void* ptr) 107 | { 108 | var span = new Span(ptr, IPV6_SIZE); 109 | Span value = stackalloc byte[IPV6_SIZE]; 110 | span.CopyTo(value); 111 | value.Reverse(); 112 | return new IPv6Address(value); 113 | } 114 | 115 | private static void SetIPAddress(void* ptr, IPAddress addr) 116 | { 117 | var span = new Span(ptr, IPV6_SIZE); 118 | addr.TryWriteBytes(span, out _); 119 | span.Reverse(); 120 | } 121 | 122 | private class IPv6Address : IPAddress 123 | { 124 | public IPv6Address(ReadOnlySpan value) 125 | : base(value) 126 | { 127 | } 128 | public override string ToString() 129 | { 130 | return base.ToString(); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertDataNetwork.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// 网络信息 5 | /// 6 | public struct WinDivertDataNetwork 7 | { 8 | /// 9 | /// 数据包到达的网络接口索引 10 | /// 11 | public int IfIdx; 12 | 13 | /// 14 | /// 数据包到达的网络子接口索引 15 | /// 16 | public int SubIfIdx; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertDataReflect.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// WinDivert信息 5 | /// 6 | public struct WinDivertDataReflect 7 | { 8 | /// 9 | /// WinDivert句柄打开时间的时间戳 10 | /// 11 | public long Timestamp; 12 | 13 | /// 14 | /// WinDivert打开句柄的进程的ID 15 | /// 16 | public int ProcessId; 17 | 18 | /// 19 | /// WinDivert工作层 20 | /// 21 | public WinDivertLayer Layer; 22 | 23 | /// 24 | /// WinDivert的Flags 25 | /// 26 | public WinDivertFlag Flags; 27 | 28 | /// 29 | /// WinDivert的优先级 30 | /// 31 | public short Priority; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertDataSocket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | 7 | namespace WindivertDotnet 8 | { 9 | /// 10 | /// Socket信息 11 | /// 12 | public unsafe struct WinDivertDataSocket 13 | { 14 | private const int IPV6_SIZE = sizeof(int) * 4; 15 | 16 | /// 17 | /// 套接字操作的终结点 ID 18 | /// 19 | public long EndpointId; 20 | 21 | /// 22 | /// 套接字操作的父终结点 ID 23 | /// 24 | public long ParentEndpointId; 25 | 26 | /// 27 | /// 与套接字操作关联的进程的 ID 28 | /// 29 | public int ProcessId; 30 | 31 | /// 32 | /// 本机地址 33 | /// 34 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 35 | private fixed byte localAddr[IPV6_SIZE]; 36 | 37 | /// 38 | /// 本机地址 39 | /// 40 | public IPAddress LocalAddr 41 | { 42 | get 43 | { 44 | fixed (void* ptr = this.localAddr) 45 | { 46 | return GetIPAddress(ptr); 47 | } 48 | } 49 | set 50 | { 51 | fixed (void* ptr = this.localAddr) 52 | { 53 | SetIPAddress(ptr, value); 54 | } 55 | } 56 | } 57 | 58 | /// 59 | /// 远程地址 60 | /// 61 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 62 | private fixed byte remoteAddr[IPV6_SIZE]; 63 | 64 | /// 65 | /// 远程地址 66 | /// 67 | public IPAddress RemoteAddr 68 | { 69 | get 70 | { 71 | fixed (void* ptr = this.remoteAddr) 72 | { 73 | return GetIPAddress(ptr); 74 | } 75 | } 76 | set 77 | { 78 | fixed (void* ptr = this.remoteAddr) 79 | { 80 | SetIPAddress(ptr, value); 81 | } 82 | } 83 | } 84 | 85 | /// 86 | /// 本机端口 87 | /// 88 | public ushort LocalPort; 89 | 90 | /// 91 | /// 远程端口 92 | /// 93 | public ushort RemotePort; 94 | 95 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 96 | private byte protocol; 97 | 98 | /// 99 | /// 协议类型 100 | /// 101 | public ProtocolType Protocol 102 | { 103 | get => (ProtocolType)protocol; 104 | set => protocol = (byte)value; 105 | } 106 | 107 | private static IPAddress GetIPAddress(void* ptr) 108 | { 109 | var span = new Span(ptr, IPV6_SIZE); 110 | Span value = stackalloc byte[IPV6_SIZE]; 111 | span.CopyTo(value); 112 | value.Reverse(); 113 | return new IPv6Address(value); 114 | } 115 | 116 | private static void SetIPAddress(void* ptr, IPAddress addr) 117 | { 118 | var span = new Span(ptr, IPV6_SIZE); 119 | addr.TryWriteBytes(span, out _); 120 | span.Reverse(); 121 | } 122 | 123 | private class IPv6Address : IPAddress 124 | { 125 | public IPv6Address(ReadOnlySpan value) 126 | : base(value) 127 | { 128 | } 129 | public override string ToString() 130 | { 131 | return base.ToString(); 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertEvent.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// 事件类型 5 | /// 6 | public enum WinDivertEvent : byte 7 | { 8 | /// 9 | /// 新的网络数据包 10 | /// 11 | NetworkPacket = 0, 12 | 13 | /// 14 | /// 将创建一个新流 15 | /// 16 | FlowEstablished = 1, 17 | 18 | /// 19 | /// 旧流将被删除 20 | /// 21 | FlowDeleted = 2, 22 | 23 | /// 24 | /// bind操作 25 | /// 26 | SocketBind = 3, 27 | 28 | /// 29 | /// connect操作 30 | /// 31 | SocketConnect = 4, 32 | 33 | /// 34 | /// listen操作 35 | /// 36 | SocketListen = 5, 37 | 38 | /// 39 | /// accept操作 40 | /// 41 | SocketAccept = 6, 42 | 43 | /// 44 | /// 套接字终结点已关闭 45 | /// 46 | SocketClose = 7, 47 | 48 | /// 49 | /// 打开了新的WinDivert实例 50 | /// 51 | ReflectOpen = 8, 52 | 53 | /// 54 | /// 旧的WinDivert实例被关闭 55 | /// 56 | ReflectClose = 9, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// WinDivert方式标志 7 | /// 8 | [Flags] 9 | public enum WinDivertFlag : ulong 10 | { 11 | /// 12 | ///不指定 13 | /// 14 | None = 0, 15 | 16 | /// 17 | ///此标志在数据包嗅探模式下打开WinDivert句柄 18 | ///在数据包嗅探模式下,原始数据包不会被丢弃和转移(默认值),而是被复制和转移 19 | ///此模式对于实现类似于当前使用的应用程序的数据包探查工具非常有用 20 | /// 21 | Sniff = 0x0001, 22 | 23 | /// 24 | ///此标志指示用户应用程序不打算使用Recv相关方法读取匹配的数据包,而应以静默方式丢弃这些数据包 25 | ///这对于使用WinDivert筛选器语言实现简单的数据包筛选器非常有用 26 | /// 27 | Drop = 0x0002, 28 | 29 | /// 30 | ///此标志强制句柄进入仅接收模式,从而有效地禁用Send相关方法的功能 31 | ///这意味着可以阻止/捕获数据包或事件,但不能注入它们 32 | /// 33 | RecvOnly = 0x0004, 34 | 35 | /// 36 | ///等同于RecvOnly 37 | /// 38 | ReadOnly = RecvOnly, 39 | 40 | /// 41 | ///此标志强制句柄进入仅发送模式,从而有效地禁用Recv相关方法 42 | ///这意味着可以注入数据包或事件,但不能阻止/捕获它们 43 | /// 44 | SendOnly = 0x0008, 45 | 46 | /// 47 | ///等同于SendOnly 48 | /// 49 | WriteOnly = SendOnly, 50 | 51 | /// 52 | ///如果尚未安装WinDivert驱动程序,开启则此标志会产生对应的异常 53 | /// 54 | NoInstall = 0x0010, 55 | 56 | /// 57 | ///开启此标记,句柄将捕获入站IP片段,但不会捕获入站重新组合的IP数据包 58 | ///否则句柄将捕获入站重新组合的IP数据包,但不会捕获入站IP片段 59 | /// 60 | Fragments = 0x0020, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertLayer.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// 工作层 5 | /// 6 | public enum WinDivertLayer : byte 7 | { 8 | /// 9 | /// 传入/传出本地计算机的网络数据包 10 | /// 11 | Network = 0, 12 | 13 | /// 14 | /// 通过本地计算机的网络数据包 15 | /// 16 | Forward = 1, 17 | 18 | /// 19 | /// 网络流已建立/已删除事件 20 | /// 21 | Flow = 2, 22 | 23 | /// 24 | /// 套接字操作事件 25 | /// 26 | Socket = 3, 27 | 28 | /// 29 | /// WinDivert处理事件 30 | /// 31 | Reflect = 4, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertOperation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | using System.Runtime.Versioning; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Threading.Tasks.Sources; 8 | 9 | namespace WindivertDotnet 10 | { 11 | /// 12 | /// Windivert控制器 13 | /// 14 | [SupportedOSPlatform("windows")] 15 | abstract class WinDivertOperation : IDisposable, IValueTaskSource 16 | { 17 | protected readonly WinDivert divert; 18 | private readonly unsafe NativeOverlapped* nativeOverlapped; 19 | 20 | private ManualResetValueTaskSourceCore taskSource; // 不能readonly 21 | private static readonly unsafe IOCompletionCallback completionCallback = new(IOCompletionCallback); 22 | 23 | 24 | /// 25 | /// Windivert控制器 26 | /// 27 | /// 28 | public unsafe WinDivertOperation(WinDivert divert) 29 | { 30 | this.divert = divert; 31 | this.nativeOverlapped = divert.GetThreadPoolBoundHandle().AllocateNativeOverlapped(completionCallback, this, null); 32 | } 33 | 34 | /// 35 | /// io控制 36 | /// 37 | /// 38 | /// 39 | public virtual async ValueTask IOControlAsync(CancellationToken cancellationToken) 40 | { 41 | cancellationToken.ThrowIfCancellationRequested(); 42 | using (cancellationToken.Register(CancelIoEx)) 43 | { 44 | return await IOControlAsync(); 45 | } 46 | 47 | unsafe void CancelIoEx() 48 | { 49 | Kernel32Native.CancelIoEx(this.divert, this.nativeOverlapped); 50 | } 51 | 52 | unsafe ValueTask IOControlAsync() 53 | { 54 | var length = 0; // 如果触发异步回调,回调里不会反写pLength,所以这里可以声明为方法内部变量 55 | return this.IOControl(&length, this.nativeOverlapped) 56 | ? new ValueTask(length) 57 | : new ValueTask(this, this.taskSource.Version); 58 | } 59 | } 60 | 61 | /// 62 | /// io控制 63 | /// 64 | /// 65 | /// 66 | /// 67 | protected abstract unsafe bool IOControl(int* pLength, NativeOverlapped* nativeOverlapped); 68 | 69 | /// 70 | /// io完成回调 71 | /// 72 | /// 73 | /// 74 | /// 75 | private static unsafe void IOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) 76 | { 77 | var operation = (WinDivertOperation)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP)!; 78 | if (errorCode == 0) 79 | { 80 | operation.taskSource.SetResult((int)numBytes); 81 | } 82 | else if (errorCode == 995) // ERROR_OPERATION_ABORTED 83 | { 84 | var exception = new TaskCanceledException(); 85 | operation.taskSource.SetException(exception); 86 | } 87 | else 88 | { 89 | var exception = new Win32Exception((int)errorCode); 90 | operation.taskSource.SetException(exception); 91 | } 92 | } 93 | 94 | /// 95 | /// 释放资源 96 | /// 97 | public virtual unsafe void Dispose() 98 | { 99 | this.divert.GetThreadPoolBoundHandle().FreeNativeOverlapped(this.nativeOverlapped); 100 | } 101 | 102 | int IValueTaskSource.GetResult(short token) 103 | { 104 | return this.taskSource.GetResult(token); 105 | } 106 | 107 | ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) 108 | { 109 | return this.taskSource.GetStatus(token); 110 | } 111 | 112 | void IValueTaskSource.OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) 113 | { 114 | this.taskSource.OnCompleted(continuation, state, token, flags); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertPacket.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Net; 6 | using System.Net.NetworkInformation; 7 | using System.Net.Sockets; 8 | using System.Runtime.CompilerServices; 9 | using System.Runtime.InteropServices; 10 | using System.Runtime.Versioning; 11 | 12 | namespace WindivertDotnet 13 | { 14 | /// 15 | /// 表示WinDivert的数据包 16 | /// 17 | [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}")] 18 | public class WinDivertPacket : SafeHandleZeroOrMinusOneIsInvalid, IEquatable, ICloneable 19 | { 20 | /// 21 | /// MTU的最大长度 22 | /// 23 | public const int MTU_MAX = 40 + 0xFFFF; 24 | 25 | /// 26 | /// 有效数据的长度 27 | /// 28 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 29 | private int length; 30 | 31 | /// 32 | /// 获取缓冲区容量 33 | /// 34 | public int Capacity { get; } 35 | 36 | /// 37 | /// 获取有效数据视图 38 | /// 39 | public unsafe Span Span => new(this.handle.ToPointer(), this.length); 40 | 41 | /// 42 | /// 获取或设置有效数据的长度 43 | /// 44 | /// 45 | public int Length 46 | { 47 | get => this.length; 48 | set 49 | { 50 | if (value < 0 || value > this.Capacity) 51 | { 52 | throw new ArgumentOutOfRangeException(nameof(Length)); 53 | } 54 | this.length = value; 55 | } 56 | } 57 | 58 | /// 59 | /// WinDivert的数据包 60 | /// 61 | /// 最大容量 62 | public WinDivertPacket(int capacity = MTU_MAX) 63 | : base(ownsHandle: true) 64 | { 65 | this.Capacity = capacity; 66 | this.handle = MemoryNative.AllocZeroed(capacity); 67 | } 68 | 69 | /// 70 | /// WinDivert的数据包 71 | /// 72 | /// 外部创建的句柄 73 | /// 字节长度 74 | private unsafe WinDivertPacket(IntPtr handle, int length) 75 | : base(ownsHandle: false) 76 | { 77 | this.handle = handle; 78 | this.length = length; 79 | this.Capacity = length; 80 | } 81 | 82 | /// 83 | /// 释放本机句柄 84 | /// 85 | /// 86 | protected override bool ReleaseHandle() 87 | { 88 | MemoryNative.Free(this.handle); 89 | return true; 90 | } 91 | 92 | /// 93 | /// 将有效数据清0 94 | /// 95 | public void Clear() 96 | { 97 | this.Span.Clear(); 98 | } 99 | 100 | /// 101 | /// 获取缓冲区的Span 102 | /// 103 | /// 偏移量 104 | /// 字节数 105 | /// 106 | /// 107 | public unsafe Span GetSpan(int offset, int count) 108 | { 109 | var pointer = this.GetPointer(offset, count); 110 | return new Span(pointer, count); 111 | } 112 | 113 | /// 114 | /// 切片 115 | /// 116 | /// 偏移量 117 | /// 大小 118 | /// 119 | public unsafe WinDivertPacket Slice(int offset, int count) 120 | { 121 | var pointer = this.GetPointer(offset, count); 122 | return new WinDivertPacket(new IntPtr(pointer), count); 123 | } 124 | 125 | 126 | /// 127 | /// 获取指针 128 | /// 129 | /// 130 | /// 131 | /// 132 | /// 133 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 134 | private unsafe void* GetPointer(int offset, int count) 135 | { 136 | if (offset < 0 || offset > this.Capacity) 137 | { 138 | throw new ArgumentOutOfRangeException(nameof(offset)); 139 | } 140 | 141 | if (count < 0 || this.Capacity - offset < count) 142 | { 143 | throw new ArgumentOutOfRangeException(nameof(count)); 144 | } 145 | 146 | return (this.handle + offset).ToPointer(); 147 | } 148 | 149 | /// 150 | /// 重新计算和修改相关的Checksums 151 | /// 当修改数据包之后需要重新计算 152 | /// 153 | /// 地址信息 154 | /// 155 | /// 156 | [SupportedOSPlatform("windows")] 157 | public bool CalcChecksums(WinDivertAddress addr, ChecksumsFlag flag = ChecksumsFlag.All) 158 | { 159 | return WinDivertNative.WinDivertHelperCalcChecksums(this, this.length, addr, flag); 160 | } 161 | 162 | /// 163 | /// 根据IP地址重新计算和修改addr的Network->IfIdx 164 | /// 当修改源IP或目标IP(不含对调)之后需要重新计算 165 | /// 166 | /// 地址信息 167 | /// 168 | /// 169 | [SupportedOSPlatform("windows")] 170 | public unsafe bool CalcNetworkIfIdx(WinDivertAddress addr) 171 | { 172 | if (addr.Layer == WinDivertLayer.Network && 173 | this.TryParseIPAddress(out _, out var dstAddr)) 174 | { 175 | addr.Network->IfIdx = WinDivertRouter.GetInterfaceIndex(dstAddr); 176 | return true; 177 | } 178 | 179 | return false; 180 | } 181 | 182 | /// 183 | /// 根据IP地址和addr.Network->IfIdx重新计算和修改addr的Outbound标记 184 | /// 当修改源IP或目标IP之后需要重新计算 185 | /// 186 | /// 地址信息 187 | /// 188 | [SupportedOSPlatform("windows")] 189 | public unsafe bool CalcOutboundFlag(WinDivertAddress addr) 190 | { 191 | if (addr.Layer != WinDivertLayer.Network || 192 | this.TryParseIPAddress(out var srcAddr, out var dstAddr) == false) 193 | { 194 | return false; 195 | } 196 | 197 | var router = new WinDivertRouter(dstAddr, srcAddr, addr.Network->IfIdx); 198 | if (router.IsOutbound == true) 199 | { 200 | addr.Flags |= WinDivertAddressFlag.Outbound; 201 | } 202 | else 203 | { 204 | addr.Flags &= ~WinDivertAddressFlag.Outbound; 205 | } 206 | return true; 207 | } 208 | 209 | /// 210 | /// 根据IP地址重新计算和修改addr的Loopback标记 211 | /// 当修改源IP或目标IP(不含对调)之后需要重新计算 212 | /// 213 | /// 地址信息 214 | /// 215 | public bool CalcLoopbackFlag(WinDivertAddress addr) 216 | { 217 | if (this.TryParseIPAddress(out var srcAddr, out var dstAddr)) 218 | { 219 | if (IPAddress.IsLoopback(srcAddr) && srcAddr.Equals(dstAddr)) 220 | { 221 | addr.Flags |= WinDivertAddressFlag.Loopback; 222 | } 223 | else 224 | { 225 | addr.Flags &= ~WinDivertAddressFlag.Loopback; 226 | } 227 | return true; 228 | } 229 | return false; 230 | } 231 | 232 | /// 233 | /// 尝试解析IP地址 234 | /// 235 | /// 236 | /// 237 | /// 238 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 239 | private unsafe bool TryParseIPAddress( 240 | [MaybeNullWhen(false)] out IPAddress srcAddr, 241 | [MaybeNullWhen(false)] out IPAddress dstAddr) 242 | { 243 | if (this.length > 1) 244 | { 245 | var ptr = this.handle.ToPointer(); 246 | var version = (IPVersion)(Unsafe.Read(ptr) >> 4); 247 | 248 | if (version == IPVersion.V4 && this.length >= sizeof(IPV4Header)) 249 | { 250 | var header = (IPV4Header*)ptr; 251 | srcAddr = header->SrcAddr; 252 | dstAddr = header->DstAddr; 253 | return true; 254 | } 255 | 256 | if (version == IPVersion.V6 && this.length >= sizeof(IPV6Header)) 257 | { 258 | var header = (IPV6Header*)ptr; 259 | srcAddr = header->SrcAddr; 260 | dstAddr = header->DstAddr; 261 | return true; 262 | } 263 | } 264 | 265 | srcAddr = default; 266 | dstAddr = default; 267 | return false; 268 | } 269 | 270 | /// 271 | /// ttl减1 272 | /// 273 | /// 274 | [SupportedOSPlatform("windows")] 275 | public bool DecrementTTL() 276 | { 277 | return WinDivertNative.WinDivertHelperDecrementTTL(this, this.length); 278 | } 279 | 280 | /// 281 | /// 获取包的哈希 282 | /// 283 | /// 284 | [SupportedOSPlatform("windows")] 285 | public override int GetHashCode() 286 | { 287 | return this.GetHashCode(seed: 0L); 288 | } 289 | 290 | /// 291 | /// 获取包的哈希 292 | /// 293 | /// 294 | /// 295 | [SupportedOSPlatform("windows")] 296 | public int GetHashCode(long seed) 297 | { 298 | return WinDivertNative.WinDivertHelperHashPacket(this, this.length, seed); 299 | } 300 | 301 | /// 302 | /// 获取缓冲区的Writer对象 303 | /// 该对象在写入数据后自动影响Length属性 304 | /// 305 | /// 偏移量 306 | /// 307 | public WindivertBufferWriter GetWriter(int offset = 0) 308 | { 309 | return new WindivertBufferWriter(this, offset); 310 | } 311 | 312 | /// 313 | /// 获取包的解析结果 314 | /// 315 | /// 316 | [SupportedOSPlatform("windows")] 317 | public unsafe WinDivertParseResult GetParseResult() 318 | { 319 | IPV4Header* pIPV4Header; 320 | IPV6Header* pIPV6Header; 321 | IcmpV4Header* pIcmpV4Header; 322 | IcmpV6Header* pIcmpV6Header; 323 | byte protocol; 324 | TcpHeader* pTcpHeader; 325 | UdpHeader* pUdpHeader; 326 | byte* pData; 327 | int dataLength; 328 | byte* pNext; 329 | int nextLength; 330 | 331 | WinDivertNative.WinDivertHelperParsePacket( 332 | this, 333 | this.length, 334 | &pIPV4Header, 335 | &pIPV6Header, 336 | &protocol, 337 | &pIcmpV4Header, 338 | &pIcmpV6Header, 339 | &pTcpHeader, 340 | &pUdpHeader, 341 | &pData, 342 | &dataLength, 343 | &pNext, 344 | &nextLength); 345 | 346 | return new WinDivertParseResult 347 | { 348 | IPV4Header = pIPV4Header, 349 | IPV6Header = pIPV6Header, 350 | Protocol = (ProtocolType)protocol, 351 | IcmpV4Header = pIcmpV4Header, 352 | IcmpV6Header = pIcmpV6Header, 353 | TcpHeader = pTcpHeader, 354 | UdpHeader = pUdpHeader, 355 | Data = pData, 356 | DataLength = dataLength, 357 | Next = pNext, 358 | NextLength = nextLength 359 | }; 360 | } 361 | 362 | /// 363 | /// 克隆 364 | /// 365 | /// 366 | public WinDivertPacket Clone() 367 | { 368 | var target = new WinDivertPacket(this.Capacity); 369 | this.CopyTo(target); 370 | return target; 371 | } 372 | 373 | object ICloneable.Clone() 374 | { 375 | return this.Clone(); 376 | } 377 | 378 | /// 379 | /// 复制数据到指定目标 380 | /// 381 | /// 382 | /// 383 | public void CopyTo(WinDivertPacket target) 384 | { 385 | target.GetWriter().Write(this.Span); 386 | } 387 | 388 | /// 389 | /// 翻转Src和Dst地址和端口 390 | /// 391 | /// 392 | [SupportedOSPlatform("windows")] 393 | public unsafe bool ReverseEndPoint() 394 | { 395 | var result = this.GetParseResult(); 396 | if (result.IPV4Header != null) 397 | { 398 | var src = result.IPV4Header->SrcAddr; 399 | result.IPV4Header->SrcAddr = result.IPV4Header->DstAddr; 400 | result.IPV4Header->DstAddr = src; 401 | } 402 | else if (result.IPV6Header != null) 403 | { 404 | var src = result.IPV6Header->SrcAddr; 405 | result.IPV6Header->SrcAddr = result.IPV6Header->DstAddr; 406 | result.IPV6Header->DstAddr = src; 407 | } 408 | else 409 | { 410 | return false; 411 | } 412 | 413 | if (result.TcpHeader != null) 414 | { 415 | var src = result.TcpHeader->SrcPort; 416 | result.TcpHeader->SrcPort = result.TcpHeader->DstPort; 417 | result.TcpHeader->DstPort = src; 418 | } 419 | 420 | if (result.UdpHeader != null) 421 | { 422 | var src = result.UdpHeader->SrcPort; 423 | result.UdpHeader->SrcPort = result.UdpHeader->DstPort; 424 | result.UdpHeader->DstPort = src; 425 | } 426 | 427 | return true; 428 | } 429 | 430 | /// 431 | /// 应用当前的Length值到IP头和Udp头 432 | /// 返回影响到Header数 433 | /// 434 | /// 435 | public unsafe int ApplyLengthToHeaders() 436 | { 437 | if (this.length < sizeof(IPV4Header)) 438 | { 439 | return 0; 440 | } 441 | 442 | var count = 0; 443 | var ptr = (byte*)this.handle.ToPointer(); 444 | var version = (IPVersion)(Unsafe.Read(ptr) >> 4); 445 | 446 | ProtocolType protocol; 447 | int ipHeaderLength; 448 | 449 | if (version == IPVersion.V4) 450 | { 451 | var header = (IPV4Header*)ptr; 452 | header->Length = (ushort)this.length; 453 | protocol = header->Protocol; 454 | ipHeaderLength = header->HdrLength * 4; 455 | count += 1; 456 | } 457 | else if (version == IPVersion.V6 && this.length >= sizeof(IPV6Header)) 458 | { 459 | var header = (IPV6Header*)ptr; 460 | header->Length = (ushort)(this.length - sizeof(IPV6Header)); 461 | protocol = header->NextHdr; 462 | ipHeaderLength = sizeof(IPV6Header); 463 | count += 1; 464 | } 465 | else 466 | { 467 | return count; 468 | } 469 | 470 | if (protocol == ProtocolType.Udp && 471 | this.length >= ipHeaderLength + sizeof(UdpHeader)) 472 | { 473 | var header = (UdpHeader*)(ptr + ipHeaderLength); 474 | header->Length = (ushort)(this.length - ipHeaderLength); 475 | count += 1; 476 | } 477 | 478 | return count; 479 | } 480 | 481 | 482 | 483 | /// 484 | /// 是否相等 485 | /// 486 | /// 487 | /// 488 | public bool Equals(WinDivertPacket? other) 489 | { 490 | return other != null && 491 | other.length == this.length && 492 | other.Span.SequenceEqual(this.Span); 493 | } 494 | 495 | /// 496 | /// 是否相等 497 | /// 498 | /// 499 | /// 500 | public override bool Equals(object? obj) 501 | { 502 | return this.Equals(obj as WinDivertPacket); 503 | } 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertParam.cs: -------------------------------------------------------------------------------- 1 | namespace WindivertDotnet 2 | { 3 | /// 4 | /// 参数枚举 5 | /// 6 | enum WinDivertParam 7 | { 8 | /// 9 | /// 列队长度 10 | /// 11 | QueueLength = 0, 12 | 13 | /// 14 | /// 列队时长 15 | /// 16 | QueueTime = 1, 17 | 18 | /// 19 | /// 列队大小 20 | /// 21 | QueueSize = 2, 22 | 23 | /// 24 | /// 主版本 25 | /// 26 | VersionMajor = 3, 27 | 28 | /// 29 | /// 次版本 30 | /// 31 | VersionMinor = 4, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertParseResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Net.Sockets; 4 | 5 | namespace WindivertDotnet 6 | { 7 | /// 8 | /// 表示包解析结果 9 | /// 10 | [DebuggerDisplay("Protocol = {Protocol}, DataLength = {DataLength}")] 11 | public sealed unsafe class WinDivertParseResult 12 | { 13 | /// 14 | /// ipv4头 15 | /// 16 | public IPV4Header* IPV4Header { get; internal set; } 17 | 18 | /// 19 | /// ipv6头 20 | /// 21 | public IPV6Header* IPV6Header { get; internal set; } 22 | 23 | /// 24 | /// 包协议 25 | /// 26 | public ProtocolType Protocol { get; internal set; } 27 | 28 | /// 29 | /// icmp4头 30 | /// 31 | public IcmpV4Header* IcmpV4Header { get; internal set; } 32 | 33 | /// 34 | /// icmp6头 35 | /// 36 | public IcmpV6Header* IcmpV6Header { get; internal set; } 37 | 38 | /// 39 | /// tcp头 40 | /// 41 | public TcpHeader* TcpHeader { get; internal set; } 42 | 43 | /// 44 | /// udp头 45 | /// 46 | public UdpHeader* UdpHeader { get; internal set; } 47 | 48 | /// 49 | /// 负载数据地址 50 | /// 即经过tcp或udp传输的数据 51 | /// 52 | public byte* Data { get; internal set; } 53 | 54 | /// 55 | /// 负载数据长度 56 | /// 57 | public int DataLength { get; internal set; } 58 | 59 | /// 60 | /// 负载数据视图 61 | /// 62 | public Span DataSpan => new(this.Data, this.DataLength); 63 | 64 | /// 65 | /// 下一块数据 66 | /// 67 | public byte* Next { get; internal set; } 68 | 69 | /// 70 | /// 下一块数据长度 71 | /// 72 | public int NextLength { get; internal set; } 73 | 74 | /// 75 | /// 下一块数据视图 76 | /// 77 | public Span NextSpan => new(this.Next, this.NextLength); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertRecvOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Runtime.Versioning; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace WindivertDotnet 7 | { 8 | [SupportedOSPlatform("windows")] 9 | sealed class WinDivertRecvOperation : WinDivertOperation 10 | { 11 | private readonly WinDivertPacket packet; 12 | private readonly WinDivertAddress addr; 13 | 14 | public WinDivertRecvOperation( 15 | WinDivert divert, 16 | WinDivertPacket packet, 17 | WinDivertAddress addr) : base(divert) 18 | { 19 | this.packet = packet; 20 | this.addr = addr; 21 | } 22 | 23 | public override async ValueTask IOControlAsync(CancellationToken cancellationToken) 24 | { 25 | var length = await base.IOControlAsync(cancellationToken); 26 | this.packet.Length = length; 27 | return length; 28 | } 29 | 30 | protected override unsafe bool IOControl(int* pLength, NativeOverlapped* nativeOverlapped) 31 | { 32 | return WinDivertNative.WinDivertRecvEx( 33 | this.divert, 34 | this.packet, 35 | this.packet.Capacity, 36 | pLength, 37 | 0UL, 38 | this.addr, 39 | null, 40 | nativeOverlapped); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.NetworkInformation; 4 | using System.Runtime.InteropServices; 5 | using System.Runtime.Versioning; 6 | 7 | namespace WindivertDotnet 8 | { 9 | /// 10 | /// 表示WinDivert路由 11 | /// 12 | [SupportedOSPlatform("windows")] 13 | public class WinDivertRouter 14 | { 15 | private const int MIB_IPFORWARD_ROW2_SIZE = 103; 16 | 17 | /// 18 | /// 获取目标地址 19 | /// 20 | public IPAddress DstAddress { get; } 21 | 22 | /// 23 | /// 获取源地址 24 | /// 25 | public IPAddress SrcAddress { get; } 26 | 27 | /// 28 | /// 获取网络接口索引 29 | /// 30 | public int InterfaceIndex { get; } 31 | 32 | /// 33 | /// 获取是否为出口方向 34 | /// 35 | public bool IsOutbound { get; } 36 | 37 | /// 38 | /// 获取是否为回环 39 | /// 40 | public bool IsLoopback { get; } 41 | 42 | /// 43 | /// WinDivert路由 44 | /// 45 | /// 目标地址 46 | /// 47 | /// 48 | /// 49 | public WinDivertRouter(IPAddress dstAddr) 50 | : this(dstAddr, srcAddr: null, interfaceIndex: null) 51 | { 52 | } 53 | 54 | /// 55 | /// WinDivert路由 56 | /// 57 | /// 目标地址 58 | /// 源地址 59 | /// 60 | /// 61 | /// 62 | public WinDivertRouter(IPAddress dstAddr, IPAddress srcAddr) 63 | : this(dstAddr, srcAddr, interfaceIndex: null) 64 | { 65 | } 66 | 67 | 68 | /// 69 | /// WinDivert路由 70 | /// 71 | /// 目标地址 72 | /// 源地址 73 | /// 网络接口索引 74 | /// 75 | /// 76 | /// 77 | /// 78 | public WinDivertRouter(IPAddress dstAddr, IPAddress srcAddr, int interfaceIndex) 79 | : this(dstAddr, srcAddr, (int?)interfaceIndex) 80 | { 81 | } 82 | 83 | /// 84 | /// WinDivert路由 85 | /// 86 | /// 目标地址 87 | /// 源地址 88 | /// 网络接口索引 89 | /// 90 | /// 91 | /// 92 | /// 93 | private unsafe WinDivertRouter(IPAddress dstAddr, IPAddress? srcAddr, int? interfaceIndex) 94 | { 95 | if (IsIPAddressAny(dstAddr)) 96 | { 97 | throw new ArgumentException($"值不能为{dstAddr}", nameof(dstAddr)); 98 | } 99 | 100 | if (srcAddr != null && srcAddr.AddressFamily != dstAddr.AddressFamily) 101 | { 102 | throw new ArgumentException($"{srcAddr}和{dstAddr}的AddressFamily不一致", nameof(srcAddr)); 103 | } 104 | 105 | if (IsIPAddressAny(srcAddr)) // any -> null 106 | { 107 | srcAddr = null; 108 | } 109 | 110 | if (InterfaceIndex < 0) 111 | { 112 | throw new ArgumentOutOfRangeException(nameof(interfaceIndex)); 113 | } 114 | 115 | SockAddress* pSrcSockAddr = null; 116 | if (srcAddr != null) 117 | { 118 | var sockAddr = new SockAddress { IPAddress = srcAddr }; 119 | pSrcSockAddr = &sockAddr; 120 | } 121 | 122 | var dstSockAddr = new SockAddress { IPAddress = dstAddr }; 123 | interfaceIndex ??= GetInterfaceIndex(ref dstSockAddr); 124 | 125 | var pBestRoute = stackalloc byte[MIB_IPFORWARD_ROW2_SIZE]; 126 | var bestSrcSockAddr = new SockAddress(); 127 | 128 | var errorCode = IPHelpApiNative.GetBestRoute2( 129 | IntPtr.Zero, 130 | interfaceIndex.Value, 131 | pSrcSockAddr, 132 | ref dstSockAddr, 133 | 0U, 134 | pBestRoute, 135 | ref bestSrcSockAddr); 136 | 137 | if (errorCode != 0 && srcAddr == null) 138 | { 139 | throw new NotSupportedException($"无法在网络接口索引{interfaceIndex}获取SrcAddress"); 140 | } 141 | 142 | this.SrcAddress = srcAddr ?? bestSrcSockAddr.IPAddress; 143 | this.DstAddress = dstAddr; 144 | this.InterfaceIndex = interfaceIndex.Value; 145 | this.IsOutbound = errorCode == 0; 146 | this.IsLoopback = IPAddress.IsLoopback(dstAddr) && dstAddr.Equals(this.SrcAddress); 147 | } 148 | 149 | /// 150 | /// 是否为any的ip 151 | /// 152 | /// 153 | /// 154 | private static bool IsIPAddressAny(IPAddress? address) 155 | { 156 | return address != null && (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any)); 157 | } 158 | 159 | /// 160 | /// 获取网络接口索引 161 | /// 162 | /// 163 | /// 164 | /// 165 | private static int GetInterfaceIndex(ref SockAddress dstSockAddr) 166 | { 167 | var errorCode = IPHelpApiNative.GetBestInterfaceEx(ref dstSockAddr, out var ifIdx); 168 | return errorCode == 0 ? ifIdx : throw new NetworkInformationException(errorCode); 169 | } 170 | 171 | /// 172 | /// 获取网络接口索引 173 | /// 174 | /// 目标地址 175 | /// 176 | /// 177 | /// 178 | public static int GetInterfaceIndex(IPAddress dstAddr) 179 | { 180 | if (IsIPAddressAny(dstAddr)) 181 | { 182 | throw new ArgumentException($"值不能为{dstAddr}", nameof(dstAddr)); 183 | } 184 | 185 | var dstSockAddr = new SockAddress { IPAddress = dstAddr }; 186 | return GetInterfaceIndex(ref dstSockAddr); 187 | } 188 | 189 | /// 190 | /// 使用路由信息创建WinDivertAddress对象 191 | /// 192 | /// 193 | public WinDivertAddress CreateAddress() 194 | { 195 | var addr = new WinDivertAddress(); 196 | this.ApplyToAddress(addr); 197 | return addr; 198 | } 199 | 200 | /// 201 | /// 应用路由信息到指定的WinDivertAddress 202 | /// 将更改Network->IfIdx、Flags.Outbound和Flags.Loopback 203 | /// 204 | /// 205 | public unsafe void ApplyToAddress(WinDivertAddress addr) 206 | { 207 | addr.Network->IfIdx = this.InterfaceIndex; 208 | 209 | if (this.IsOutbound) 210 | { 211 | addr.Flags |= WinDivertAddressFlag.Outbound; 212 | } 213 | else 214 | { 215 | addr.Flags &= ~WinDivertAddressFlag.Outbound; 216 | } 217 | 218 | if (this.IsLoopback) 219 | { 220 | addr.Flags |= WinDivertAddressFlag.Loopback; 221 | } 222 | else 223 | { 224 | addr.Flags &= ~WinDivertAddressFlag.Loopback; 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertSendOperation.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Runtime.Versioning; 3 | using System.Threading; 4 | 5 | namespace WindivertDotnet 6 | { 7 | [SupportedOSPlatform("windows")] 8 | sealed class WinDivertSendOperation : WinDivertOperation 9 | { 10 | private readonly WinDivertPacket packet; 11 | private readonly WinDivertAddress addr; 12 | 13 | public WinDivertSendOperation( 14 | WinDivert divert, 15 | WinDivertPacket packet, 16 | WinDivertAddress addr) : base(divert) 17 | { 18 | this.packet = packet; 19 | this.addr = addr; 20 | } 21 | 22 | protected override unsafe bool IOControl(int* pLength, NativeOverlapped* nativeOverlapped) 23 | { 24 | return WinDivertNative.WinDivertSendEx( 25 | this.divert, 26 | this.packet, 27 | this.packet.Length, 28 | pLength, 29 | 0UL, 30 | this.addr, 31 | WinDivertAddress.Size, 32 | nativeOverlapped); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /WindivertDotnet/WinDivertShutdown.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WindivertDotnet 4 | { 5 | /// 6 | /// 关闭内容 7 | /// 8 | [Flags] 9 | public enum WinDivertShutdown 10 | { 11 | /// 12 | /// 接收 13 | /// 14 | Recv = 0x1, 15 | 16 | /// 17 | /// 发送 18 | /// 19 | Send = 0x2, 20 | 21 | /// 22 | /// 接收和发送 23 | /// 24 | Both = Recv | Send 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WindivertDotnet/WindivertBufferWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace WindivertDotnet 7 | { 8 | /// 9 | /// Windivert缓冲区Writer 10 | /// 11 | public class WindivertBufferWriter : IBufferWriter 12 | { 13 | private int index; 14 | private readonly WinDivertPacket packet; 15 | 16 | /// 17 | /// Windivert缓冲区Writer 18 | /// 19 | /// 数据包 20 | /// 偏移量 21 | public WindivertBufferWriter(WinDivertPacket packet, int offset) 22 | { 23 | this.packet = packet; 24 | this.index = offset; 25 | } 26 | 27 | /// 28 | /// 将值写入并翻转字节顺序 29 | /// 30 | /// 31 | /// 翻转之前的值 32 | public unsafe void WriteReverse(TValue value) where TValue : unmanaged 33 | { 34 | var span = this.GetSpan(sizeof(TValue))[..sizeof(TValue)]; 35 | Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), value); 36 | span.Reverse(); 37 | this.Advance(sizeof(TValue)); 38 | } 39 | 40 | /// 41 | /// 写入值 42 | /// 43 | /// 44 | /// 值 45 | public unsafe void Write(TValue value) where TValue : unmanaged 46 | { 47 | var span = this.GetSpan(sizeof(TValue)); 48 | Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), value); 49 | this.Advance(sizeof(TValue)); 50 | } 51 | 52 | /// 53 | /// 写入值 54 | /// 55 | /// 值 56 | /// 57 | public void Write(ReadOnlySpan value) 58 | { 59 | value.CopyTo(this.GetSpan(value.Length)); 60 | this.Advance(value.Length); 61 | } 62 | 63 | /// 64 | /// 写入字节 65 | /// 66 | /// 值 67 | /// 68 | public void Write(byte value) 69 | { 70 | const int count = 1; 71 | this.GetSpan(count)[0] = value; 72 | this.Advance(count); 73 | } 74 | 75 | /// 76 | /// 向前推进 77 | /// 78 | /// 字节数 79 | /// 80 | public void Advance(int count) 81 | { 82 | var size = this.index + count; 83 | this.packet.Length = size; 84 | this.index = size; 85 | } 86 | 87 | /// 88 | /// 获取预期大小的写入缓冲区 89 | /// 90 | /// 预期大小 91 | /// 92 | /// 93 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 94 | public Span GetSpan(int sizeHint = 0) 95 | { 96 | if (sizeHint <= 0) 97 | { 98 | sizeHint = this.packet.Capacity - this.index; 99 | } 100 | return this.packet.GetSpan(this.index, sizeHint); 101 | } 102 | 103 | /// 104 | /// 获取预期大小的写入缓冲区 105 | /// 106 | /// 107 | /// 108 | /// 109 | Memory IBufferWriter.GetMemory(int sizeHint) 110 | { 111 | throw new NotSupportedException(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /WindivertDotnet/WindivertDotnet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1.1.2 5 | netcoreapp3.1;net6.0 6 | enable 7 | CA2012;IDE0079 8 | 9.0 9 | True 10 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 11 | True 12 | WindivertDotnet 13 | 面向对象的Windivert的dotnet封装 14 | https://github.com/xljiulang/WindivertDotnet 15 | https://github.com/xljiulang/WindivertDotnet 16 | license 17 | icon.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | True 28 | \ 29 | 30 | 31 | True 32 | \ 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /WindivertDotnet/v222/x64/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/v222/x64/WinDivert.dll -------------------------------------------------------------------------------- /WindivertDotnet/v222/x64/WinDivert64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/v222/x64/WinDivert64.sys -------------------------------------------------------------------------------- /WindivertDotnet/v222/x86/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/v222/x86/WinDivert.dll -------------------------------------------------------------------------------- /WindivertDotnet/v222/x86/WinDivert32.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/v222/x86/WinDivert32.sys -------------------------------------------------------------------------------- /WindivertDotnet/v222/x86/WinDivert64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/v222/x86/WinDivert64.sys -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/icon.png --------------------------------------------------------------------------------