上篇文章主要是讓 Wireshark 軟體事先擷取好封包,將擷取記錄儲存成 *.pcap 檔之後,再寫程式去分析 *.pcap 檔的記錄內容。
不過有些狀況會希望能夠即時擷取網卡上的資料,這時就可以利用 jNetPcap 函式庫提供的 openLive() 方法了。
以下是範例程式碼。在這個範例中,會先嘗試列舉出所有電腦可用的網卡(扣除 Lookback 以及部分虛擬網卡)
並且依序把網卡丟去做即時攔截。
攔截下來的封包會先被過濾,只留下由本機向外發出的封包
並嘗試用 HTTP 協定來解析,在 log 上印出本機發出的 HTTP 封包要發送的目的地。
import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jnetpcap.Pcap;
import org.jnetpcap.PcapAddr;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;
import org.jnetpcap.protocol.network.Ip4;
import org.jnetpcap.protocol.tcpip.Http;
public class WebMailPcap {
private static Logger log = LogManager.getLogger("WebMailPcap");
public static void main(String[] args) {
try {
Path outputPath = Paths.get("D:", "test", "output");
log.debug("Output path: " + outputPath.toString());
WebMailPcap test = new WebMailPcap();
test.capture(outputPath);
} catch (Exception | Error e) {
e.printStackTrace();
}
}
/**
* Capture live data from network interfaces.
* @param outputFolder
*/
public void capture (Path outputFolder) {
StringBuilder errbuf = new StringBuilder();
LinkedList<PcapIf> infList = new LinkedList<>();
if(Pcap.findAllDevs(infList, errbuf) < 0)
log.error("Cannot find available interfaces.");
else {
Iterator<PcapIf> iter = infList.iterator();
while(iter.hasNext()) {
PcapIf netInf = iter.next();
log.debug("Find network interface '" + netInf.getName() + "' (" + netInf.getDescription() + ").");
String description = netInf.getDescription().toLowerCase();
if(!description.contains("virtual") && description.split(" ").length > 1)
capture(netInf, outputFolder);
}
}
}
/**
* Capture the bytes from a specified network interface, and stores the attachments to the specified output folder.
* @param netInf Network interface to be captured.
* @param outputFolder Folder for storing the attachments.
*/
public void capture (final PcapIf netInf, Path outputFolder) {
log.debug("Try to capture packets from network interface interface '" + netInf.getName() + "' (" + netInf.toString() + ").");
/***************************************************************************
* First we setup error buffer and name for our file
**************************************************************************/
// For any error
final StringBuilder errbuf = new StringBuilder();
// Parser for parsing packet using HTTP protocol.
final Http http = new Http();
// Truncate packet at this size.
int snaplen = 2048;
// Set the interface in 'Promiscuous Mode' which capture all packets with or without destination address points to it.
int promiscous = Pcap.MODE_PROMISCUOUS;
// In milliseconds
int timeout = 60000;
/***************************************************************************
* Second we open up the selected file using openOffline call
**************************************************************************/
Pcap pcap = Pcap.openLive(netInf.getName(), snaplen, promiscous, timeout, errbuf);
// Error occurred while accessing the specified *.pcap file.
if (pcap == null) {
log.error("Error while opening device for capture: " + errbuf.toString());
return;
}
/***************************************************************************
* Third we create a packet handler which will receive packets from
* the libpcap loop.
**************************************************************************/
// Collection for storing the Internet addresses of the specified interface.
final Set<String> ifAddrs = new HashSet<String>(netInf.getAddresses().size());
for(PcapAddr addr : netInf.getAddresses()) {
try {
// Parse the Internet address and cache it in the memory.
String capturedAddr = InetAddress.getByAddress(addr.getAddr().getData()).getHostAddress();
ifAddrs.add(capturedAddr);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
PcapPacketHandler<Path> jpacketHandler = new PcapPacketHandler<Path>() {
/**
* Get the next packet from the captured file.
* @param packet Captured packet.
* @param outputPath Path for outputting the captured result.
*/
public void nextPacket(PcapPacket packet, Path outputPath) {
// --------------------------------------------------------- //
// Filter packets which is sent from local host. //
// --------------------------------------------------------- //
Ip4 ip4 = new Ip4();
if (packet.hasHeader(ip4)) {
try {
String srcAddr = InetAddress.getByAddress(ip4.source()).getHostAddress();
if (!ifAddrs.contains(srcAddr))
return;
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
// ----------------------------------------------------------------------------- //
// Filter packets which is using TCP protocol and is a HTTP request. //
// ----------------------------------------------------------------------------- //
if (packet.hasHeader(http)) {
if (http.hasField(Http.Request.Accept)) {
String host = http.fieldValue(Http.Request.Host);
log.debug("Capture a HTTP request to '" + host + "'.");
}
}
}
};
try {
pcap.loop(Pcap.LOOP_INFINITE, jpacketHandler, outputFolder);
} catch (Exception | Error e) {
e.printStackTrace();
} finally {
pcap.close();
}
}
}
上面的程式碼中,比較需要注意的地方是,44 ~ 56 行是列舉電腦上可用的網卡
這裡該用的列舉方法並不是 Java 內建的方法,而是 Pcap 提供的方法
如果使用 Java 內建的 NetworkInterface.getNetworkInterfaces() 方法去列舉的話,Pcap 函式庫會在初始化時出現錯誤而停止執行。
參考資料:
1、Opening a Network Interface for Capture
2、Capturing network packets using jNetPcap API
沒有留言:
張貼留言