1.jpcap说明与安装
JAVA语言虽然在TCP/UDP传输方面给予了良好的定义,但对于网络层以下的控制,却是无能为力的。JPCAP扩展包弥补了这一点,jPcap是一个可以让java工作在链路层的类库;当然,它底层还是使用了本机API通过Jini调用,在javaAPI中得到数据。JPCAP实际上并非一个真正去实现对数据链路层的控制,而是一个中间件,JPCAP调用wincap/libpcap,而给JAVA语言提供一个公共的接口,从而实现了平台无关性。在官方网站上声明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP等系统。jPcap下载地址:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html ; 你可以从jpcap网站上直接下载它的桌面应用程序进行测试,可以全面的统计本机的网络数据流量及收发包数据。
本项目中使用的是jPcap0.6版本,从其网站上下载Source build后,可以看到其下详细的目录结构,源码,例程及Native lib。
使用jPcap可以编写出功能完备的网络嗅测程序,本节中,我们只是使用其非常简单的一个功能:统计本机每块网卡上收发数据的总量。
特别注意:jpcap运行时依赖winCap的类库,使用前必须在机地安装winCap(http://www.winpcap.org/ )(如果是在liunx上,则请到http://www.tcpdump.org/ 下载)。本节中jPcap版本为0.6,winCap版本为4.0,运行与win32系统上。
2.jPcap小试:显示本机网络接口详情
jPcap中的API非常简单,可查看其在线文档:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html 。当然,要灵活的使用,你必须有良好的tcp/ip协议知识基础,对常用的3个关键类,简介如下:
JpcapCaptor类
static NetworkInterface[]getDeviceList()
这个静态方法调用,可以返回机器上网络接口卡对象的数组,数组中每一个NetworkInterface元素对象代表一个网络接口;一般使用jPcap所要做的第一步调用就是这个方法。
static JpcapCaptor openDevice(NetworkInterface interface, int snaplen, boolean promisc, int to_ms)
取得在指定网卡上的Jpcapcator对象,Interface:上所返回的某个网卡对象Snaplen:一次性要抓取数据包的最大长度。 Promisc:设置是否混杂模式。处于混杂模式将接收所有数据包,如果设置为混杂模式后调用了包过滤函数setFilter()将不起任何作用; To_ms:这个参数主要用于processPacket()方法,指定超时的时间;
int loopPacket(int count, PacketReceiver handler)
常用的一种模式是,通过getDeviceList()取得所有网络接口,再通过openDevice方法取得每个网络接口上的JpcapCaptor对象,就可通过这个方法抓包了。loopPacket方法中count参数表示要抓的包的数目,如果设备为-1,责表示永远抓下去---方法不会返回;第二个参数必须是实现了PacketReceiver接口的一个对象,抓到的包将调用这个PacketReceiver对象中的receivePacket(Packet packet)方法处理;所以抓包前,我们必须写一个实现了PacketReceiver接口的类。 特别注意的是:这个方法的调用会阻塞等待,如果没有抓到指定count的包、或count设为-1,这个方法都不会返回。所以,聪明的你肯定想到了,如果要抓取机器上多个卡口上的包,这个方法必须放在一个独立的线程中。
void breakLoop()
即上JpcapCaptor对象上阻塞等待的方法强制终止。当调用processPacket()和loopPacket()后,再调用这个方法可以强制让processPacket()和loopPacket()停止。
interface PacketReceiver :
这个接口中仅有一个方法定义:
Void receivePacket (Packet p)
实现类中处理接收到的Packet对象的方法。每个Packet对象代表从热指定网络接口上抓取到的数据包。
NetworkInterface类
NetworkInterfaceAddress[]addresses
这个接口的网络地址。设定为数组应该是考虑到有些设备同时连接多条线路,例如路由器。但我们的PC机的网卡一般只有一条线路,所以我们一般取addresses[0]就够了。
java.lang.String datalink_description.
数据链路层的描述。描述所在的局域网是什么网。例如,以太网(Ethernet)、无线LAN网(wireless LAN)、令牌环网(token ring)等等。
java.lang.String datalink_name datalink_name
该网络设备所对应数据链路层的名称。具体来说,例如Ethernet10M、100M、1000M等等。
java.lang.String description
网卡是XXXX牌子XXXX型号之类的描述。例如我的网卡描述:Realtek RTL8169/8110 Family Gigabit Ethernet NIC
boolean Loopback 标志这个设备是否loopback设备。
byte[]mac_address 网卡的MAC地址,6个字节。
java.lang.String Name 这个设备的名称。例如我的网卡名称:\Device\NPF_{3CE5FDA5-E15D-4F87-B217-255BCB351CD5}
jPcap的API使用很简单,如下代码示例:显示机器上的所有网络接口DispalyNetInterface.java:
不幸的是,这段代码运行时会报如下错误:
还记得我们介绍jPcap: “它底层还是使用了本机API通过Jini调用,在javaAPI中得到数据”,这是因为当前cp中少了jpcap要调用的本地dll库。将jpacp下载后lib下面的jpcap.dll复制到当前项目目录下,如果你仅得制了jpcap.dll,操作系统没有安装wincap(http://www.winpcap.org/install/default.htm ),则会看到如下错误提示:
配置完好后,再次运行,输出结果正常:
第0个接口:|name: \Device\NPF_GenericDialupAdapter|loopback: false
address: 第1个接口:|name: \Device\NPF_{2A5FD532-45A3-4A2B-9B68-F34C14E4FD2C}|loopback: falseaddress: |addresses[0]: /220.192.159.105第2个接口:|name: \Device\NPF_{14303C1A-4DB3-4BC9-979E-34063E070CBB}|loopback: falseaddress: |addresses[0]: /192.168.1.44
在我的机器上,开着一块局网网卡和一块无线网卡,都显示出来了;上面的”第0个接口”就是指本机的127.0.0.1的回环地址。但不知为什么,loopback也会是false?
要注意两点:一个是jpcap.dll要在路径中;另外,在统计流量时,本机回环地址不需要统计,一般是第0个接口。接下来我们看抓取网卡上的数据包是多么简单:
抓包前,首先要编写实现了PacketReceiver接口的类,即数据包处理器,由与抓包时,对应某个网络接口的JpcapCaptor对象会阻塞,所以我们将每个网卡上得到的对应的JpcapCaptor对象放到一个独立线程中运行;TestPacketReceiver是个数据包解析器,本例中我们只是简单打印出收到的数据包类型及关键参数,阅读如下代码建议参照jPcap的在线文档(http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html )
TestPacketReceiver.java代码如下:
运行这段程序,你机器从网络收所有收发到的数据,就都可以展现在你眼前了!,在我的机器上,摘抄一段输出如下:
注意:你可能认为要抓取的数据包应分为发送出去的和接收到的;但对jPcap而言,数据包是从网卡上抓取到的一个packet对象,packet没有收发的概念;如果你要确定这个packet的方向,则可根据其中的属性数据值,如源地址、目标地址确认。如果想深入的研究网络协议就请继续钻研吧,我们的任务可仅仅只是统计网络流量,demo了这么多,赶快实现我们的数据统计模块。
JAVA语言虽然在TCP/UDP传输方面给予了良好的定义,但对于网络层以下的控制,却是无能为力的。JPCAP扩展包弥补了这一点,jPcap是一个可以让java工作在链路层的类库;当然,它底层还是使用了本机API通过Jini调用,在javaAPI中得到数据。JPCAP实际上并非一个真正去实现对数据链路层的控制,而是一个中间件,JPCAP调用wincap/libpcap,而给JAVA语言提供一个公共的接口,从而实现了平台无关性。在官方网站上声明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP等系统。jPcap下载地址:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html ; 你可以从jpcap网站上直接下载它的桌面应用程序进行测试,可以全面的统计本机的网络数据流量及收发包数据。
- JpcapisaJavaclasspackagethatallowsJavaapplicationstocaptureand/orsendpacketstothenetwork.
- Jpcapisbasedonlibpcap/winpcapandRawSocketAPI.Therefore,JpcapissupposedtoworkonanyOSonwhichlibpcap/winpcaphasbeenimplemented.Currently,JpcaphasbeentestedonFreeBSD3.x,LinuxRedHat6.1,FedoraCore4,Solaris,andMicrosoftWindows2000/XP.
- Jpcapsupportsthefollowingtypesofpackets:Ethernet,IPv4,IPv6,ARP/RARP,TCP,UDP,andICMPv4.Othertypesofpacketsarecapturedasrawpackets(i.e.,instancesofthePacketclass)whichcontainsthewholedataofthepackets.ThisallowsJavaapplicationstoanalyzeunsupportedpackettypes.
本项目中使用的是jPcap0.6版本,从其网站上下载Source build后,可以看到其下详细的目录结构,源码,例程及Native lib。
使用jPcap可以编写出功能完备的网络嗅测程序,本节中,我们只是使用其非常简单的一个功能:统计本机每块网卡上收发数据的总量。
特别注意:jpcap运行时依赖winCap的类库,使用前必须在机地安装winCap(http://www.winpcap.org/ )(如果是在liunx上,则请到http://www.tcpdump.org/ 下载)。本节中jPcap版本为0.6,winCap版本为4.0,运行与win32系统上。
2.jPcap小试:显示本机网络接口详情
jPcap中的API非常简单,可查看其在线文档:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html 。当然,要灵活的使用,你必须有良好的tcp/ip协议知识基础,对常用的3个关键类,简介如下:
JpcapCaptor类
这个类是jPcap中的核心对象,一个JpcapCaptor对象代表了了系统中的一个网络接口卡;通过对JpcapCaptor对象的调用,实现网络数据包的抓取和发送。它供了一系列静态方法调用如:获取网卡列表,获取某个网卡上的JpcapCaptor对象。 |
static NetworkInterface[]getDeviceList()
这个静态方法调用,可以返回机器上网络接口卡对象的数组,数组中每一个NetworkInterface元素对象代表一个网络接口;一般使用jPcap所要做的第一步调用就是这个方法。
static JpcapCaptor openDevice(NetworkInterface interface, int snaplen, boolean promisc, int to_ms)
取得在指定网卡上的Jpcapcator对象,Interface:上所返回的某个网卡对象Snaplen:一次性要抓取数据包的最大长度。 Promisc:设置是否混杂模式。处于混杂模式将接收所有数据包,如果设置为混杂模式后调用了包过滤函数setFilter()将不起任何作用; To_ms:这个参数主要用于processPacket()方法,指定超时的时间;
int loopPacket(int count, PacketReceiver handler)
常用的一种模式是,通过getDeviceList()取得所有网络接口,再通过openDevice方法取得每个网络接口上的JpcapCaptor对象,就可通过这个方法抓包了。loopPacket方法中count参数表示要抓的包的数目,如果设备为-1,责表示永远抓下去---方法不会返回;第二个参数必须是实现了PacketReceiver接口的一个对象,抓到的包将调用这个PacketReceiver对象中的receivePacket(Packet packet)方法处理;所以抓包前,我们必须写一个实现了PacketReceiver接口的类。 特别注意的是:这个方法的调用会阻塞等待,如果没有抓到指定count的包、或count设为-1,这个方法都不会返回。所以,聪明的你肯定想到了,如果要抓取机器上多个卡口上的包,这个方法必须放在一个独立的线程中。
void breakLoop()
即上JpcapCaptor对象上阻塞等待的方法强制终止。当调用processPacket()和loopPacket()后,再调用这个方法可以强制让processPacket()和loopPacket()停止。
interface PacketReceiver :
数据包处理器接口定义,要处理收到的数据包,必须编写这个接口的实现类,在JpcapCaptor对象的loopPacket方法中调用. |
这个接口中仅有一个方法定义:
Void receivePacket (Packet p)
实现类中处理接收到的Packet对象的方法。每个Packet对象代表从热指定网络接口上抓取到的数据包。
NetworkInterface类
NetworkInterface类该类的每一个实例代表一个网络设备,一般就是网卡。这个类只有一些数据成员,除了继承自java.lang.Object的基本方法以外,没有定义其它方法。(但我还不知它与jdk5.0以上的API中的java.net.InterfaceAddress类是否可以互换)。 |
NetworkInterfaceAddress[]addresses
这个接口的网络地址。设定为数组应该是考虑到有些设备同时连接多条线路,例如路由器。但我们的PC机的网卡一般只有一条线路,所以我们一般取addresses[0]就够了。
java.lang.String datalink_description.
数据链路层的描述。描述所在的局域网是什么网。例如,以太网(Ethernet)、无线LAN网(wireless LAN)、令牌环网(token ring)等等。
java.lang.String datalink_name datalink_name
该网络设备所对应数据链路层的名称。具体来说,例如Ethernet10M、100M、1000M等等。
java.lang.String description
网卡是XXXX牌子XXXX型号之类的描述。例如我的网卡描述:Realtek RTL8169/8110 Family Gigabit Ethernet NIC
boolean Loopback 标志这个设备是否loopback设备。
byte[]mac_address 网卡的MAC地址,6个字节。
java.lang.String Name 这个设备的名称。例如我的网卡名称:\Device\NPF_{3CE5FDA5-E15D-4F87-B217-255BCB351CD5}
jPcap的API使用很简单,如下代码示例:显示机器上的所有网络接口DispalyNetInterface.java:
- importjpcap.JpcapCaptor;
- importjpcap.NetworkInterface;
- importjpcap.PacketReceiver;
- importjpcap.packet.Packet;
- /**
- *使用jpcap显示网络接口数据.
- */
- publicclassDispalyNetInterface{
- publicstaticvoidmain(Stringargs[]){
- try{
- //获取本机上的网络接口对象数组
- finalNetworkInterface[]devices=JpcapCaptor.getDeviceList();
- for(inti=0;i<devices.length;i++){
- NetworkInterfacenc=devices[i];
- //一块卡上可能有多个地址:
- Stringaddress="";
- for(intt=0;t<nc.addresses.length;t++){
- address+="|addresses["+t+"]:"+nc.addresses[t].address.toString();
- }
- //打印说明:
- System.out.println("第"+i+"个接口:"+"|name:"+nc.name
- +"|loopback:"+nc.loopback+"\r\naddress:"+address);
- }
- }catch(Exceptionef){
- ef.printStackTrace();
- System.out.println("显示网络接口数据失败:"+ef);
- }
- }
- }
不幸的是,这段代码运行时会报如下错误:
- Exceptioninthread"main"java.lang.UnsatisfiedLinkError:nojpcapinjava.library.path
- atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)atjava.lang.Runtime.loadLibrary0(Runtime.java:823)atjava.lang.System.loadLibrary(System.java:1030)atjpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250)atcn.netjava.cewolf.DispalyNetInterface.main(DispalyNetInterface.java:19)
还记得我们介绍jPcap: “它底层还是使用了本机API通过Jini调用,在javaAPI中得到数据”,这是因为当前cp中少了jpcap要调用的本地dll库。将jpacp下载后lib下面的jpcap.dll复制到当前项目目录下,如果你仅得制了jpcap.dll,操作系统没有安装wincap(http://www.winpcap.org/install/default.htm ),则会看到如下错误提示:
- Exceptioninthread"main"java.lang.UnsatisfiedLinkError:E:\workspace\trafficManager\jpcap.dll:Can'tfinddependentlibraries
- atjava.lang.ClassLoader$NativeLibrary.load(NativeMethod)atjava.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751)atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1676)atjava.lang.Runtime.loadLibrary0(Runtime.java:823)atjava.lang.System.loadLibrary(System.java:1030)atjpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250)atcn.netjava.cewolf.DispalyNetInterface.main(DispalyNetInterface.java:17)
配置完好后,再次运行,输出结果正常:
第0个接口:|name: \Device\NPF_GenericDialupAdapter|loopback: false
address: 第1个接口:|name: \Device\NPF_{2A5FD532-45A3-4A2B-9B68-F34C14E4FD2C}|loopback: falseaddress: |addresses[0]: /220.192.159.105第2个接口:|name: \Device\NPF_{14303C1A-4DB3-4BC9-979E-34063E070CBB}|loopback: falseaddress: |addresses[0]: /192.168.1.44
在我的机器上,开着一块局网网卡和一块无线网卡,都显示出来了;上面的”第0个接口”就是指本机的127.0.0.1的回环地址。但不知为什么,loopback也会是false?
要注意两点:一个是jpcap.dll要在路径中;另外,在统计流量时,本机回环地址不需要统计,一般是第0个接口。接下来我们看抓取网卡上的数据包是多么简单:
抓包前,首先要编写实现了PacketReceiver接口的类,即数据包处理器,由与抓包时,对应某个网络接口的JpcapCaptor对象会阻塞,所以我们将每个网卡上得到的对应的JpcapCaptor对象放到一个独立线程中运行;TestPacketReceiver是个数据包解析器,本例中我们只是简单打印出收到的数据包类型及关键参数,阅读如下代码建议参照jPcap的在线文档(http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html )
TestPacketReceiver.java代码如下:
- importjpcap.JpcapCaptor;
- importjpcap.NetworkInterface;
- importjpcap.PacketReceiver;
- importjpcap.packet.*;
- /**
- *使用jpcap显示网络上的各种数据包
- *@authorwww.NetJava.cn
- */
- publicclassDispalyNetPacket{
- //程序启动主方法
- publicstaticvoidmain(Stringargs[]){
- try{
- //获取本机上的网络接口对象数组
- finalNetworkInterface[]devices=JpcapCaptor.getDeviceList();
- for(inti=0;i<devices.length;i++){
- NetworkInterfacenc=devices[i];
- //创建某个卡口上的抓取对象,最大为2000个
- JpcapCaptorjpcap=JpcapCaptor.openDevice(nc,2000,true,20);
- startCapThread(jpcap);
- System.out.println("开始抓取第"+i+"个卡口上的数据");
- }
- }catch(Exceptionef){
- ef.printStackTrace();
- System.out.println("启动失败:"+ef);
- }
- }
- //将每个Captor放到独立线程中运行
- publicstaticvoidstartCapThread(finalJpcapCaptorjpcap){
- JpcapCaptorjp=jpcap;
- java.lang.Runnablernner=newRunnable(){
- publicvoidrun(){
- //使用接包处理器循环抓包
- jpcap.loopPacket(-1,newTestPacketReceiver());
- }
- };
- newThread(rnner).start();//启动抓包线程
- }
- }
- /**
- *抓包监听器,实现PacketReceiver中的方法:打印出数据包说明
- *@authorwww.NetJava.cn
- */
- classTestPacketReceiverimplementsPacketReceiver{
- /**
- *实现的接包方法:
- */
- publicvoidreceivePacket(Packetpacket){
- //Tcp包,在javaSocket中只能得到负载数据
- if(packetinstanceofjpcap.packet.TCPPacket){
- TCPPacketp=(TCPPacket)packet;
- Strings="TCPPacket:|dst_ip"+p.dst_ip+":"+p.dst_port
- +"|src_ip"+p.src_ip+":"+p.src_port
- +"|len:"+p.len;
- System.out.println(s);
- }
- //UDP包,开着QQ,你就会看到:它是tcp+udp
- elseif(packetinstanceofjpcap.packet.UDPPacket){
- UDPPacketp=(UDPPacket)packet;
- Strings="UDPPacket:|dst_ip"+p.dst_ip+":"+p.dst_port
- +"|src_ip"+p.src_ip+":"+p.src_port
- +"|len:"+p.len;
- System.out.println(s);
- }
- //如果你要在程序中构造一个ping报文,就要构建ICMPPacket包
- elseif(packetinstanceofjpcap.packet.ICMPPacket){
- ICMPPacketp=(ICMPPacket)packet;
- //ICMP包的路由链
- Stringrouter_ip="";
- for(inti=0;i<p.router_ip.length;i++){
- router_ip+=""+p.router_ip[i].getHostAddress();
- }
- Strings="@@@ICMPPacket:|router_ip"+router_ip
- +"|redir_ip:"+p.redir_ip
- +"|mtu:"+p.mtu
- +"|length:"+p.len;
- System.out.println(s);
- }
- //是否地址转换协议请求包
- elseif(packetinstanceofjpcap.packet.ARPPacket){
- ARPPacketp=(ARPPacket)packet;
- //Returnsthehardwareaddress(MACaddress)ofthesender
- Objectsaa=p.getSenderHardwareAddress();
- Objecttaa=p.getTargetHardwareAddress();
- Strings="***ARPPacket:|SenderHardwareAddress"+saa
- +"|TargetHardwareAddress"+taa
- +"|len:"+p.len;
- System.out.println(s);
- }
- //取得链路层数据头:如果你想局网抓包或伪造数据包,嘿嘿
- DatalinkPacketdatalink=packet.datalink;
- //如果是以太网包
- if(datalinkinstanceofjpcap.packet.EthernetPacket){
- EthernetPacketep=(EthernetPacket)datalink;
- Strings="datalinklayerpacket:"
- +"|DestinationAddress:"+ep.getDestinationAddress()
- +"|SourceAddress:"+ep.getSourceAddress();
- System.out.println(s);
- }
- }
- }
运行这段程序,你机器从网络收所有收发到的数据,就都可以展现在你眼前了!,在我的机器上,摘抄一段输出如下:
- datalinklayerpacket:|DestinationAddress:01:00:5e:00:00:01|SourceAddress:00:19:e0:f0:ee:55
- datalinklayerpacket:|DestinationAddress:01:00:5e:26:4c:da|SourceAddress:00:e0:81:03:7c:01TCPPacket:|dst_ip/128.195.10.200:80|src_ip/192.168.1.44:1083|len:62datalinklayerpacket:|DestinationAddress:00:19:e0:f0:ee:55|SourceAddress:00:02:8a:96:d1:ab***ARPPacket:|SenderHardwareAddress00:19:e0:f0:ee:55|TargetHardwareAddress00:00:00:00:00:00|len:60datalinklayerpacket:|DestinationAddress:ff:ff:ff:ff:ff:ff|SourceAddress:00:19:e0:f0:ee:55***ARPPacket:|SenderHardwareAddress00:02:8a:96:d1:ab|TargetHardwareAddress00:19:e0:f0:ee:55|len:42datalinklayerpacket:|DestinationAddress:00:19:e0:f0:ee:55|SourceAddress:00:02:8a:96:d1:abTCPPacket:|dst_ip/192.168.1.44:1083|src_ip/128.195.10.200:80|len:62datalinklayerpacket:|DestinationAddress:00:02:8a:96:d1:ab|SourceAddress:00:19:e0:f0:ee:55
注意:你可能认为要抓取的数据包应分为发送出去的和接收到的;但对jPcap而言,数据包是从网卡上抓取到的一个packet对象,packet没有收发的概念;如果你要确定这个packet的方向,则可根据其中的属性数据值,如源地址、目标地址确认。如果想深入的研究网络协议就请继续钻研吧,我们的任务可仅仅只是统计网络流量,demo了这么多,赶快实现我们的数据统计模块。