http://jnetpcap.com/examples
在上一期的栏目中我们介绍了通过Fiddler嗅探Http协议网络数据包的方法,并且在文章最后通过开心农场的例子来展示网络嗅探的基本操作。但手工获得数据毕竟耗时耗力,颇为麻烦,不妨将这个工作交给电脑,写一个程序让电脑在点击好友的时候自动嗅探到信息数据包并进行处理。这期我们就来介绍一下如何在Java平台下通过第三方包来进行底层网络嗅探。
Java平台本身不支持底层网络操作,需要第三方包利用JNI封装不同系统的C库来提供Java的上层接口。常用的类库包括JPcap,JNetPcap等,他们都是基于TcpDump/LibPcap的Java封装。其中JPcap已经一年多没更新了,而JNetPcap (jnetpcap.com) 在上周刚刚发布了1.2 RC5版本,添加了很多实用的类库,诸如高级协议分析等,本文就以JNetPcap作为例子来进行介绍。JNetPcap无疑是当前最强大以及最具有潜力的网络数据包捕捉类库。感谢Mark B. 的辛苦工作,让Java的JNI世界更加精彩。
我们经常使用Httpwatch查看HTTP的传输数据。那java能不能做到呢? 下面我们就讲一下如何通过java在windows上获取网卡的数据包,这里我们使用了开源的WinPcap,还有jNetPcap,jNetPcap是对接了WinPcap来截获网卡数据包。
如何截获网卡分3步:
1. 在自己的机器上安装WinPcap。 http://www.winpcap.org/install/default.htm
2. 下载jNetPcap, http://jnetpcap.com/download. 下载下来之后解压,里面2个重要文件jnetpcap.jar,jnetpcap.dll
3 在eclipse里新建工程,把jnetpcap.jar加入library. 然后参考如下代码实现网卡截包:
import java.util.ArrayList;
import java.util.List;
import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.JPacket;
import org.jnetpcap.packet.JPacketHandler;
import org.jnetpcap.protocol.tcpip.Http;
import org.jnetpcap.protocol.tcpip.Tcp;
/**
* Capture the netword card packages
*
*/
public class App {
public static void main(String[] args) throws InterruptedException {
List
// NICs
StringBuilder errbuf = new StringBuilder();
int r = Pcap.findAllDevs(alldevs, errbuf);
if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
System.err.printf("Can't read list of devices, error is %s",
errbuf.toString());
return;
}
for (PcapIf pif : alldevs) {
System.out.println(pif.getName());
}
PcapIf pif = alldevs.get(0);//select the device which you want to monitor
/***************************************
* open the device
***************************************/
int snaplen = 64 * 1024; // Capture all packets, no trucation
int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
int timeout = 10 * 1000; // 10 seconds in millis
Pcap pcap = Pcap.openLive(pif.getName(), snaplen, flags, timeout,
errbuf);
if (pcap == null) {
System.err.printf("Error while opening device for capture: "
+ errbuf.toString());
return;
}
/*
* We have an opened the capture file now time to read packets. We use a
* Pcap.loop function to retrieve 10 packets from the file. We supply an
* annonymous handler which will receive packets as they are read from the
* offline file by libpcap. We parameterize it with a StringBuilder class.
* This allows us to pass in any type of object we need inside the our
* dispatch handler. For this example we are passing in the errorbuf object
* so we can pass back a string, if we need to. Of course in our example
* this is not strictly needed since our anonymous class can access errbuf
* object directly from the enclosing main method as that local variable is
* marked final allowing anonymous classes access to it.
*/
pcap.loop(Pcap.LOOP_INFINITE, new JPacketHandler
/**
* We purposely define and allocate our working tcp header (accessor)
* outside the dispatch function and thus the libpcap loop, as this type
* of object is reusable and it would be a very big waist of time and
* resources to allocate it per every dispatch of a packet. We mark it
* final since we do not plan on allocating any other instances of Tcp.
*/
final Tcp tcp = new Tcp();
/*
* Same thing for our http header
*/
final Http http = new Http();
/**
* Our custom handler that will receive all the packets libpcap will
* dispatch to us. This handler is inside a libpcap loop and will receive
* exactly 10 packets as we specified on the Pcap.loop(10, ...) line
* above.
*
* @param packet
* a packet from our capture file
* @param errbuf
* our custom user parameter which we chose to be a StringBuilder
* object, but could have chosen anything else we wanted passed
* into our handler by libpcap
*/
public void nextPacket(JPacket packet, StringBuilder errbuf) {
/*
* Here we receive 1 packet at a time from the capture file. We are
* going to check if we have a tcp packet and do something with tcp
* header. We are actually going to do this twice to show 2 different
* ways how we can check if a particular header exists in the packet and
* then get that header (peer header definition instance with memory in
* the packet) in 2 separate steps.
*/
if (packet.hasHeader(Tcp.ID)) {
/*
* Now get our tcp header definition (accessor) peered with actual
* memory that holds the tcp header within the packet.
*/
packet.getHeader(tcp);
System.out.printf("tcp.dst_port=%d%n", tcp.destination());
System.out.printf("tcp.src_port=%d%n", tcp.source());
System.out.printf("tcp.ack=%x%n", tcp.ack());
}
/*
* An easier way of checking if header exists and peering with memory
* can be done using a conveniece method JPacket.hasHeader(? extends
* JHeader). This method performs both operations at once returning a
* boolean true or false. True means that header exists in the packet
* and our tcp header difinition object is peered or false if the header
* doesn't exist and no peering was performed.
*/
if (packet.hasHeader(tcp)) {
//System.out.printf("tcp header::%s%n", tcp.toString());
}
/*
* A typical and common approach to getting headers from a packet is to
* chain them as a condition for the if statement. If we need to work
* with both tcp and http headers, for example, we place both of them on
* the command line.
*/
if (packet.hasHeader(tcp) && packet.hasHeader(http)) {
/*
* Now we are guarranteed to have both tcp and http header peered. If
* the packet only contained tcp segment even though tcp may have http
* port number, it still won't show up here since headers appear right
* at the beginning of http session.
*/
System.out.printf("http header::%s%n", http);
/*
* jNetPcap keeps track of frame numbers for us. The number is simply
* incremented with every packet scanned.
*/
}
//System.out.printf("frame #%d%n", packet.getFrameNumber());
}
}, errbuf);
/*
* Last thing to do is close the pcap handle
*/
pcap.close();
}
}
注意:
在运行以上代码之前还需要加上jvm参数,为了让jnetpcap找到jnetpcap.dll,我们在vm parameters加入以下参数:
-Djava.library.path=E:\jnetpcap
这里的E:\jnetpcap 就是jnetpcap.dll放置的目录。