上篇文章主要是讓 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
沒有留言:
張貼留言