Npcap与Pcap4J
文章目录
- 1 介绍
- 1_Npcap介绍
- 2_Pcap4J介绍
- 2 下载并安装 Npcap
- 3 编写Java程序进行测试
- 4 补充说明
1 介绍
1_Npcap介绍
Npcap 是用于 Windows 操作系统的数据包捕获和网络分析的架构,由软件库和网络驱动程序组成。
大多数网络应用程序通过广泛使用的操作系统原语(例如 Socket 套接字)访问网络。
通过这种方式访问网络上的数据非常容易,因为操作系统会处理底层细节(例如协议处理、数据包重组等),并提供类似于读写文件的熟悉接口。
然而,有时这种“简单的方法”并不适合,因为有些应用程序需要直接访问网络上的数据包。也就是说,它们需要访问网络上的“原始”数据,而无需操作系统介入协议处理。
Npcap 的目的是为 Windows 应用程序提供此类访问权限。它提供以下功能:
- 捕获原始数据包,包括发往运行它的机器的数据包和与其他主机(在共享媒体上)交换的数据包
- 根据用户指定的规则过滤数据包,然后再将其发送给应用程序
- 向网络传输原始数据包
- 收集网络流量的统计信息
Npcap 官网:https://npcap.com/。
如果是 Linux 系统则使用 libpcap。
2_Pcap4J介绍
Pcap4J 是一个用于捕获、处理和发送数据包的 Java 库。Pcap4J 通过JNA封装了原生数据包捕获库(libpcap、 WinPcap或Npcap) ,并提供了面向 Java 的 API。
简单来说就是 Pcap4J 可以为我们的 Java 程序提供调用 Npcap 访问传输层以下网络接口的能力。
Pcap4J 官网:https://www.pcap4j.org/。
接下来简单了解一下如何进行使用。
2 下载并安装 Npcap
下载并安装最新版 Npcap。
打开下载的安装程序勾选如下两个选项安装即可:
- “Install Npcap in WinPcap API-compatible Mode”
- “Support raw 802.11 traffic (and monitor mode) for wireless adapters”
3 编写Java程序进行测试
通信思路说明:
- 发送端构造一整个 Ethernet + IP + UDP 数据包。
- 接收端在链路层捕获包并解析 UDP 载荷。
- 不需要 socket,不依赖 Java 内核传输栈。
添加 Maven 依赖:
<dependency><groupId>org.pcap4j</groupId><artifactId>pcap4j-core</artifactId><version>1.8.2</version>
</dependency>
通过如下程序获取本机所有网卡信息:
import lombok.Data;import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;public class NetworkInfoUtil {private static final List<Mac> macs = new ArrayList<>();@Datapublic static class Mac {private String interfaceName;private String interfaceDesc;private String macAddr;private String ipv4Addr;}static {try {Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();while (interfaces.hasMoreElements()) {Mac mac = new Mac();NetworkInterface ni = interfaces.nextElement();// 排除回环、禁用、虚拟网卡if (ni.isLoopback() || !ni.isUp() || ni.isVirtual()) {continue;}mac.setInterfaceName(ni.getName());mac.setInterfaceDesc(ni.getDisplayName());// 获取 MAC 地址byte[] macBytes = ni.getHardwareAddress();if (macBytes != null) {StringBuilder macAddr = new StringBuilder();for (int i = 0; i < macBytes.length; i++) {macAddr.append(String.format("%02X%s", macBytes[i], (i < macBytes.length - 1) ? "-" : ""));}mac.setMacAddr(macAddr.toString());} else {System.out.println("MAC 地址: 无法获取");}// 获取 IPv4 地址Enumeration<InetAddress> inetAddresses = ni.getInetAddresses();while (inetAddresses.hasMoreElements()) {InetAddress addr = inetAddresses.nextElement();if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {mac.setIpv4Addr(addr.getHostAddress());}}macs.add(mac);System.out.println(mac);System.out.println("--------------------------");}} catch (SocketException ignored) {}}public static Mac getMacRandom() {Random random = new Random();int index = random.nextInt(macs.size());return macs.get(index);}
}
服务端程序,抓取数据包:
import org.pcap4j.core.*;import java.util.List;public class RawUdpReceiver {public static void main(String[] args) throws Exception {// 1. 获取网络接口列表List<PcapNetworkInterface> allDevs = Pcaps.findAllDevs();// 选择第一个接口 (通常修改为你的目标网卡索引)PcapNetworkInterface nif = allDevs.get(0); // 可以根据描述筛选特定网卡// 2. 打开接口 - 关键设置 snapLen 和 promiscuous mode// snapLen = 65536; 捕获完整包PcapHandle handle = nif.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 50);handle.setFilter("udp port 54321", BpfProgram.BpfCompileMode.OPTIMIZE);// 3. 设置抓包回调 (这里简单打印包到达时间和长度)PacketListener listener = packet -> {System.out.println("收到数据包:" + packet);};try {// 4. 开始抓包 (持续抓10个包)handle.loop(10, listener); } catch (Exception e) {System.out.println("捕获完毕");}// 5. 清理handle.close();}
}
客户端程序,发送数据包
import org.pcap4j.core.*;
import org.pcap4j.packet.*;
import org.pcap4j.packet.namednumber.EtherType;
import org.pcap4j.packet.namednumber.IpNumber;
import org.pcap4j.packet.namednumber.IpVersion;
import org.pcap4j.packet.namednumber.UdpPort;
import org.pcap4j.util.MacAddress;import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;public class RawUdpSender {public static void main(String[] args) throws PcapNativeException, NotOpenException, UnknownHostException {// 1. 获取并选择网络接口PcapNetworkInterface nif = Pcaps.findAllDevs().get(0);; // 或者你网卡的名称,例如 "eth0"if (nif == null) {System.out.println("找不到网卡");return;}// 2. 打开接口 (混杂模式)PcapHandle handle = nif.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);// 3. 构造 一个简单的以太网帧 Ethernet 包NetworkInfoUtil.Mac macRandom = NetworkInfoUtil.getMacRandom();String macAddr = macRandom.getMacAddr();MacAddress srcMac = MacAddress.getByName(macAddr);MacAddress dstMac = MacAddress.getByName(macAddr);String ipv4Addr = macRandom.getIpv4Addr();InetAddress srcIp = InetAddress.getByName(ipv4Addr);InetAddress dstIp = InetAddress.getByName(ipv4Addr);//载荷 - 这里简单放点数据byte[] udpPayload = "你好,这是一段原始UDP包".getBytes();// 构建 UDP 数据包UdpPacket.Builder udpBuilder = new UdpPacket.Builder();udpBuilder.srcAddr(srcIp).dstAddr(dstIp).srcPort(UdpPort.getInstance((short) 12345)).dstPort(UdpPort.getInstance((short) 54321)).correctChecksumAtBuild(true).correctLengthAtBuild(true).payloadBuilder(new UnknownPacket.Builder().rawData(udpPayload));// IPIpV4Packet.Builder ipBuilder = new IpV4Packet.Builder();ipBuilder.version(IpVersion.IPV4).tos(IpV4Rfc791Tos.newInstance((byte) 0)).ttl((byte) 64).protocol(IpNumber.UDP).srcAddr((Inet4Address) srcIp).dstAddr((Inet4Address) dstIp).payloadBuilder(udpBuilder).correctChecksumAtBuild(true).correctLengthAtBuild(true);// 构建以太帧EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder();etherBuilder.dstAddr(dstMac).srcAddr(srcMac).type(EtherType.IPV4).payloadBuilder(ipBuilder).paddingAtBuild(true);// 4. 发送数据包handle.sendPacket(etherBuilder.build());System.out.println("发送成功");// 5. 清理handle.close();}
}
运行结果如下:
4 补充说明
更多使用说明详见:https://github.com/kaitoy/pcap4j。