oioio-space/pkt
GitHub: oioio-space/pkt
一个纯 Go 实现的跨平台数据包捕获与操控库,在 Windows 上通过内嵌的 WinDivert 驱动实现数据包拦截、修改和丢弃,在 Linux 上通过 AF_PACKET 实现无 CGo 依赖的被动嗅探,全部接口兼容 gopacket。
Stars: 0 | Forks: 0
# pkt
用于跨平台数据包捕获的 Go 多模块工作区。提供 Windows(通过 WinDivert)上的被动嗅探、主动数据包拦截、注入和修改,以及 Linux(通过 AF_PACKET)上的被动嗅探。所有数据源均实现了 `gopacket.PacketDataSource`。
[](https://pkg.go.dev/github.com/oioio-space/pkt/capture)
[](https://pkg.go.dev/github.com/oioio-space/pkt/windivert)
[](https://pkg.go.dev/github.com/oioio-space/pkt/afpacket)
[](https://pkg.go.dev/github.com/oioio-space/pkt/bpf)
状态:正常工作,已在 Windows 11 和 Linux (amd64) 上测试。
## 安装说明
每个模块独立进行版本控制。只需安装您所需的模块:
```
# Cross-platform capture API (推荐起点)
go get github.com/oioio-space/pkt/capture@latest
# WinDivert — Windows 数据包拦截 (通过 Windows 上的 capture 包含)
go get github.com/oioio-space/pkt/windivert@latest
# AF_PACKET — Linux raw socket 捕获 (通过 Linux 上的 capture 包含)
go get github.com/oioio-space/pkt/afpacket@latest
# BPF — 为 AF_PACKET sockets 编译 pcap-filter 表达式
go get github.com/oioio-space/pkt/bpf@latest
```
## 功能
- 跨平台捕获 API (`pkt/capture`),只需一次 `Open(iface, filter)` 调用
- Windows:WinDivert 2.2.2 — 在网络层拦截、丢弃、修改和重新注入数据包
- Linux:带有 `SO_TIMESTAMP` 的 AF_PACKET — 结合内核 BPF 过滤器的被动嗅探
- 兼容 `gopacket`:所有数据源均实现了 `gopacket.PacketDataSource`
- 内嵌 WinDivert64.sys 2.2.2 — 默认无需单独安装驱动程序
- Linux 上的 BPF 内核过滤器,Windows 上的 WinDivert 过滤表达式
- pcap 导出兼容 Wireshark
## 为什么不使用 libpcap?
libpcap(及其 Go 封装库 `gopacket/pcap`)是最常见的数据包捕获库。它可以在 Windows(通过 Npcap)和 Linux 上运行,且支持良好。**如果您只需要被动嗅探,libpcap 是一个完全没问题的选择。**
此项目的存在是为了应对 libpcap 显得力不从心的使用场景:
| 能力 | libpcap / Npcap | AF_PACKET (Linux) | WinDivert (Windows) |
|---|---|---|---|
| 读取数据包 | 是 | 是 | 是 |
| **在传输中丢弃数据包** | **否** | **否** | **是** |
| **在传输中修改数据包** | **否** | **否** | **是** |
| **真正的重新注入(同一数据包)** | **否** | **否** | **是** |
| 发送独立的原始数据包 | 是 (`pcap_inject`) | 是 (`sendto`) | 是 |
| 捕获层 | **L2 — 以太网帧** | **L2 — 以太网帧** | **L3 — IP 数据包** |
| 查看非 IP 流量 (ARP, LLDP…) | 是 | 是 | 否 |
| 查看 MAC 地址 | 是 | 是 | 否 |
| 内核级过滤 | 是 (BPF) | 是 (BPF) | 是 (WinDivert 过滤器) |
| CGo 依赖 | **是** | 否 (此库) | **否** — 纯 Go |
| Windows 驱动程序 | 需安装 Npcap/WinPcap | — | **内嵌**,在运行时解压 |
| 交叉编译 (`CGO_ENABLED=0`) | 否 | **是** | **是** |
### 副本与拦截
libpcap 和 AF_PACKET 交付每个数据包的**副本** — 原始数据包不受影响地流经网络栈。`pcap_inject()` 和 AF_PACKET 的 `sendto()` 可以发送*独立的*原始数据包,但原始数据包绝不会被拦截或修改。
WinDivert 在 Windows Filtering Platform (WFP) **拦截**数据包:该数据包会被**拦截**,直到您的代码调用 `h.Send()` 将其重新注入(可选择修改),或者通过不调用 `Send` 来丢弃它。这是一种根本不同的模型,可实现:
- **防火墙** — 丢弃匹配过滤器的数据包
- **流量整形器 / 代理** — 拦截、重写并重新注入
- **协议模糊测试** — 在传输过程中损坏字段以进行测试
- **透明隧道** — 捕获、加密、转发、重新注入
### 层级差异:L2 与 L3
libpcap 和 AF_PACKET 在 **Layer 2** 运行 — 它们交付原始以太网帧,包括 MAC 地址、VLAN 标签和非 IP 协议(ARP、LLDP 等)。
处于 `LayerNetwork` 的 WinDivert 在 **Layer 3** 运行 — 它交付没有以太网报头的 IP 数据包。MAC 地址和 ARP 不可见。如果您在 Windows 上需要 L2 可见性,libpcap/Npcap 才是合适的工具。
### 在 Windows 网络栈中的位置
在 Windows 上,Npcap 和 WinDivert 都挂接到 **Windows Filtering Platform (WFP)**,但在不同的层级:
```
Application
│
[ WinSock / TCP/IP stack ]
│
┌───┴────────────────────────────────┐
│ Windows Filtering Platform (WFP) │
│ │
│ ← WinDivert callout driver │ intercepts here — packet is held
│ (FWPM_LAYER_OUTBOUND_IPPACKET…) │
└───┬────────────────────────────────┘
│
[ NDIS — Network Driver Interface ]
│
┌───┴──────────────────────────┐
│ Npcap NDIS filter driver │ copies here — original flows through
└───┬──────────────────────────┘
│
Network interface card (NIC)
```
Npcap 位于 WFP **下方**的 NDIS 层 — 它在 WFP 决策做出后接收原始以太网帧的副本。它无法影响路由或防火墙决策。
WinDivert 注册一个 **WFP 呼出** — 它在防火墙决策流水线*内部*运行。内核会等待 `h.Send()` 操作才会转发数据包,这就是为什么可以进行丢弃和修改的原因。
### 无 CGo,无外部依赖
libpcap 需要 CGo 和一个系统库(Linux 上的 `libpcap.so`,Windows 上的 `Npcap.dll`)。这使交叉编译、Docker 镜像和纯粹的 Windows 部署变得复杂。
`pkt` 全程保持 `CGO_ENABLED=0`。WinDivert64.sys 驱动程序被内嵌在二进制文件中并在运行时解压 — 无需单独的安装步骤。
### 何时使用什么
| 情况 | 建议 |
|---|---|
| 在 Linux 上嗅探流量,需要 Ethernet/MAC/ARP | `gopacket/pcap` 或 `pkt/afpacket` |
| 在 Linux 上嗅探 IP 流量,无 CGo | `pkt/afpacket` |
| 在 Windows 上嗅探流量 | `pkt/capture` 或 `gopacket/pcap` |
| 在 Windows 上需要 MAC 地址 / ARP | `gopacket/pcap` (Npcap, L2) |
| **在 Windows 上丢弃 / 阻断数据包** | **`pkt/windivert`** — 唯一真正的选项 |
| **在 Windows 上修改传输中的数据包** | **`pkt/windivert`** — 唯一真正的选项 |
| 零 CGo,从 Linux 交叉编译到 Windows | **`pkt`** |
| 已经到处都在使用 libpcap | 继续使用 `gopacket/pcap` |
## 查找网络接口
**Windows (PowerShell):**
```
Get-NetAdapter | Select Name, InterfaceIndex, Status
# or
ipconfig
```
**Windows (cmd):**
```
netsh interface show interface
```
**Linux:**
```
ip link show
# or
ls /sys/class/net/
```
## 用法示例
### 1. 嗅探器 — 跨平台 (`pkt/capture`)
```
import "github.com/oioio-space/pkt/capture"
// Linux — interface required
src, err := capture.Open("eth0", "tcp port 443")
// Windows — interface ignored, WinDivert filter syntax
// src, err := capture.Open("", "tcp.DstPort == 443")
if err != nil {
log.Fatal(err)
}
defer src.Close()
ps := gopacket.NewPacketSource(src, src.LinkType())
for pkt := range ps.Packets() {
fmt.Println(pkt)
}
```
### 2. 嗅探器 — 直接使用 WinDivert (`pkt/windivert`)
```
import "github.com/oioio-space/pkt/windivert"
h, err := windivert.OpenSniff("tcp.DstPort == 443", windivert.LayerNetwork)
// equivalent: windivert.Open("tcp.DstPort == 443", windivert.LayerNetwork, windivert.WithFlags(windivert.FlagSniff))
if err != nil {
log.Fatal(err)
}
defer h.Close()
ps := gopacket.NewPacketSource(h, h.LinkType())
for pkt := range ps.Packets() {
fmt.Println(pkt)
}
```
### 3. 丢弃数据包 (Windows)
```
h, err := windivert.Open("tcp.DstPort == 443", windivert.LayerNetwork)
if err != nil {
log.Fatal(err)
}
defer h.Close()
ps := gopacket.NewPacketSource(h, h.LinkType())
for range ps.Packets() {
// Don't call h.Send() — kernel discards the packet
}
```
### 4. 重新注入 / 转发数据包 (Windows)
```
h, err := windivert.Open("tcp", windivert.LayerNetwork)
if err != nil {
log.Fatal(err)
}
defer h.Close()
ps := gopacket.NewPacketSource(h, h.LinkType())
for pkt := range ps.Packets() {
addr := windivert.AddressFromPacket(pkt)
if addr == nil {
continue
}
// optionally modify pkt.Data() here
_ = h.Send(pkt.Data(), addr)
}
```
### 5. 写入 pcap 文件
```
f, _ := os.Create("out.pcap")
bw := bufio.NewWriterSize(f, 1<<20)
defer bw.Flush()
defer f.Close()
w := pcapgo.NewWriter(bw)
_ = w.WriteFileHeader(65535, layers.LinkTypeIPv4)
src, _ := capture.Open("", "tcp") // Windows — iface ignored
defer src.Close()
ps := gopacket.NewPacketSource(src, src.LinkType())
for pkt := range ps.Packets() {
ci := pkt.Metadata().CaptureInfo
_ = w.WritePacket(ci, pkt.Data())
}
```
### 6. 永久安装 WinDivert 驱动程序 (Windows,需管理员权限)
```
import (
"github.com/oioio-space/pkt/windivert"
"github.com/oioio-space/pkt/windivert/driver"
)
// Install driver permanently (SERVICE_AUTO_START, stable path in System32\drivers\)
err := windivert.InstallDriver(driver.WithPersistent())
```
永久安装后,随后对 `windivert.Open` 的调用将跳过 SCM 安装步骤,并直接打开设备。
### 7. 获取内嵌驱动程序版本
```
fmt.Println(windivert.DriverVersion) // "2.2.2"
```
## WinDivert 过滤器语法
WinDivert 过滤器是在数据包字段上求值的类 C 布尔表达式:
```
tcp # all TCP packets
tcp.DstPort == 443 # TCP to port 443
ip.SrcAddr == 192.168.1.1 # from specific IP
tcp and not tcp.DstPort == 80 # TCP except port 80
ip.TTL < 5 # low TTL
outbound and tcp # outbound TCP only
not loopback # exclude loopback
true # all packets
```
常见字段:
| 字段 | 描述 |
|-------|-------------|
| `tcp`, `udp`, `icmp`, `ip`, `ipv6` | 协议谓词 |
| `tcp.SrcPort`, `tcp.DstPort` | TCP 端口号 |
| `udp.SrcPort`, `udp.DstPort` | UDP 端口号 |
| `ip.SrcAddr`, `ip.DstAddr` | IPv4 地址 |
| `ipv6.SrcAddr`, `ipv6.DstAddr` | IPv6 地址 |
| `ip.TTL`, `ip.Protocol` | IP 报头字段 |
| `tcp.Syn`, `tcp.Ack`, `tcp.Fin`, `tcp.Rst` | TCP 标志 |
| `outbound`, `inbound` | 方向 |
| `loopback` | 回环接口 |
| `ifIdx` | 接口索引 |
完整参考:https://reqrypt.org/windivert-doc.html#filter_language
## Linux BPF 过滤器语法 (pcap-filter)
```
tcp port 443 # TCP port 443 (src or dst)
tcp dst port 80 # TCP destination port 80
host 192.168.1.1 # traffic to/from host
src host 10.0.0.1 # traffic from host
net 192.168.1.0/24 # subnet
tcp and not port 22 # TCP except SSH
udp port 53 # DNS
```
完整参考:`man pcap-filter`
## 构建
**Linux:**
```
make linux # build Linux capture binary → dist/capture-linux-amd64
make check # build + vet
make test # run unit tests (no root required)
make setcap # grant CAP_NET_RAW to the Linux binary (requires sudo)
```
**Windows (PowerShell):**
```
.\build.ps1 # build all (Linux + Windows binaries)
.\build.ps1 -Target windows # Windows binaries only
.\build.ps1 -Target linux # Linux binary only (cross-compile)
.\build.ps1 -Target check # build + vet
.\build.ps1 -Target test # run unit tests
.\build.ps1 -Target clean # remove dist/
```
构建输出会放入 `dist/` 目录。所有二进制文件均为静态链接 (`CGO_ENABLED=0`)。
## 模块结构
```
pkt/
├── afpacket/ # Linux raw socket capture (AF_PACKET + SO_TIMESTAMP + BPF)
├── bpf/ # BPF filter compiler (used by afpacket on Linux)
├── capture/ # Cross-platform unified capture API (wraps windivert/afpacket)
├── windivert/ # Windows kernel driver bindings (WinDivert 2.2.2)
│ ├── assets/ # Embedded WinDivert64.sys
│ ├── driver/ # SCM driver installer (temporary + persistent modes)
│ └── filter/ # WinDivert filter expression compiler (bytecode)
└── examples/
└── cmd/
├── capture/ # Cross-platform sniffer + pcap writer
├── drop/ # Windows: drop packets matching a filter
├── filter/ # Windows: firewall (blacklist / whitelist)
└── modify-payload/ # Windows: rewrite TCP payload in-flight
```
有关标志和用法,请参阅每个示例的 README:
- [`examples/cmd/capture`](examples/cmd/capture/README.md) — 跨平台嗅探器,pcap 导出
- [`examples/cmd/drop`](examples/cmd/drop/README.md) — Windows 数据包丢弃工具
- [`examples/cmd/filter`](examples/cmd/filter/README.md) — Windows 防火墙(黑名单/白名单)
- [`examples/cmd/modify-payload`](examples/cmd/modify-payload/README.md) — Windows TCP 负载修改器
标签:AF_PACKET, BPF过滤, DNS枚举, EVTX分析, gopacket, Go语言, pcap, WinDivert, Windows 11, XML 请求, 中间人攻击工具, 内核驱动, 原始套接字, 多模块工作区, 安全开发, 数据包修改, 数据包拦截, 数据包注入, 日志审计, 流量劫持, 目录遍历, 程序破解, 系统底层, 网络协议, 网络安全工具, 网络层, 网络抓包, 网络编程, 防御绕过