jpcap網絡抓包

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網站上直接下載它的桌面應用程序進行測試,可以全面的統計本機的網絡數據流量及收發包數據。 
Java代碼  收藏代碼
  1. Jpcap is a Java class package that allows Java applications to capture and/or send packets to the network.  
  2. Jpcap is based on libpcap/winpcap and Raw Socket API. Therefore, Jpcap is supposed to work on any OS on which libpcap/winpcap has been implemented. Currently, Jpcap has been tested on FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP.  
  3. Jpcap supports the following types of packets: Ethernet, IPv4, IPv6, ARP/RARP, TCP, UDP, and ICMPv4.  Other types of packets are captured as raw packets (i.e., instances of the Packet class) which contains the whole data of the packets.  This allows Java applications to analyze unsupported packet types.  

本項目中使用的是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: 

Java代碼  收藏代碼
  1. import jpcap.JpcapCaptor;    
  2. import jpcap.NetworkInterface;    
  3. import jpcap.PacketReceiver;    
  4. import jpcap.packet.Packet;    
  5. /**  
  6.  * 使用jpcap顯示網絡接口數據.  
  7.  */    
  8. public class DispalyNetInterface {    
  9.         
  10.         
  11.    public static void main(String args[]){    
  12.        try{    
  13.  //獲取本機上的網絡接口對象數組    
  14.  final NetworkInterface[] devices = JpcapCaptor.getDeviceList();    
  15.         for(int i=0;i<devices.length;i++){    
  16.      NetworkInterface nc=devices[i];    
  17.      //一塊卡上可能有多個地址:    
  18.      String address="";    
  19.  for(int t=0;t<nc.addresses.length;t++){    
  20. address+="|addresses["+t+"]: "+nc.addresses[t].address.toString();    
  21.               }    
  22. //打印說明:    
  23.  System.out.println("第"+i+"個接口:"+"|name: "+nc.name    
  24. +"|loopback: "+nc.loopback+"\r\naddress: "+address);    
  25.         }    
  26.              
  27.         }catch(Exception ef){    
  28.             ef.printStackTrace();    
  29.      System.out.println("顯示網絡接口數據失敗:  "+ef);    
  30.     }    
  31.    }    
  32. }    




不幸的是,這段代碼運行時會報如下錯誤: 
Java代碼  收藏代碼
  1. Exception in thread "main" java.lang.UnsatisfiedLinkError: no jpcap in java.library.path  
  2.     at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)    at java.lang.Runtime.loadLibrary0(Runtime.java:823)    at java.lang.System.loadLibrary(System.java:1030)    at jpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250)    at cn.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 ),則會看到如下錯誤提示: 
Java代碼  收藏代碼
  1. Exception in thread "main" java.lang.UnsatisfiedLinkError: E:\workspace\trafficManager\jpcap.dll: Can't find dependent libraries  
  2.     at java.lang.ClassLoader$NativeLibrary.load(Native Method)    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751)    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1676)    at java.lang.Runtime.loadLibrary0(Runtime.java:823)    at java.lang.System.loadLibrary(System.java:1030)    at jpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250)    at cn.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代碼如下: 

Java代碼  收藏代碼
  1. import jpcap.JpcapCaptor;    
  2. import jpcap.NetworkInterface;    
  3. import jpcap.PacketReceiver;    
  4. import jpcap.packet.*;    
  5. /**  
  6.  * 使用jpcap顯示網絡上的各種數據包  
  7.  * @author www.NetJava.cn   
  8.  */    
  9. public class DispalyNetPacket {    
  10.         
  11.     //程序啓動主方法    
  12.    public static void main(String args[]){    
  13.        try{    
  14.             //獲取本機上的網絡接口對象數組    
  15.      final  NetworkInterface[] devices = JpcapCaptor.getDeviceList();    
  16.         for(int i=0;i<devices.length;i++){    
  17.             NetworkInterface nc=devices[i];    
  18.            //創建某個卡口上的抓取對象,最大爲2000個    
  19.         JpcapCaptor jpcap = JpcapCaptor.openDevice(nc, 2000true20);    
  20.         startCapThread(jpcap);    
  21.         System.out.println("開始抓取第"+i+"個卡口上的數據");    
  22.         }    
  23.         }catch(Exception ef){    
  24.             ef.printStackTrace();    
  25.             System.out.println("啓動失敗:  "+ef);    
  26.         }    
  27.     
  28.    }    
  29.     //將每個Captor放到獨立線程中運行    
  30.    public static void startCapThread(final JpcapCaptor jpcap ){    
  31.        JpcapCaptor jp=jpcap;    
  32.        java.lang.Runnable rnner=new Runnable(){    
  33.            public void run(){    
  34.                //使用接包處理器循環抓包    
  35.                jpcap.loopPacket(-1new TestPacketReceiver());    
  36.            }    
  37.        };    
  38.        new Thread(rnner).start();//啓動抓包線程    
  39.    }        
  40. }    
  41.     
  42. /**  
  43.  * 抓包監聽器,實現PacketReceiver中的方法:打印出數據包說明  
  44.  * @author www.NetJava.cn   
  45.  */    
  46. class TestPacketReceiver  implements PacketReceiver {    
  47.       /**  
  48.        * 實現的接包方法:  
  49.        */    
  50.       public void receivePacket(Packet packet) {    
  51.           //Tcp包,在java Socket中只能得到負載數據    
  52.         if(packet instanceof jpcap.packet.TCPPacket){    
  53.             TCPPacket p=(TCPPacket)packet;    
  54.             String s="TCPPacket:| dst_ip "+p.dst_ip+":"+p.dst_port    
  55.                      +"|src_ip "+p.src_ip+":"+p.src_port    
  56.                      +" |len: "+p.len;    
  57.         System.out.println(s);    
  58.         }    
  59.         //UDP包,開着QQ,你就會看到:它是tcp+udp    
  60.         else if(packet instanceof jpcap.packet.UDPPacket){    
  61.             UDPPacket p=(UDPPacket)packet;    
  62.             String s="UDPPacket:| dst_ip "+p.dst_ip+":"+p.dst_port    
  63.              +"|src_ip "+p.src_ip+":"+p.src_port    
  64.             +" |len: "+p.len;    
  65.            System.out.println(s);    
  66.         }    
  67.         //如果你要在程序中構造一個ping報文,就要構建ICMPPacket包    
  68.        else if(packet instanceof jpcap.packet.ICMPPacket){    
  69.            ICMPPacket p=(ICMPPacket)packet;    
  70.            //ICMP包的路由鏈    
  71.            String router_ip="";    
  72.            for(int i=0;i<p.router_ip.length;i++){    
  73.                router_ip+=" "+p.router_ip[i].getHostAddress();    
  74.            }    
  75.             String s="@ @ @ ICMPPacket:| router_ip "+router_ip    
  76.              +" |redir_ip: "+p.redir_ip    
  77.              +" |mtu: "+p.mtu    
  78.              +" |length: "+p.len;    
  79.           System.out.println(s);    
  80.         }    
  81.         //是否地址轉換協議請求包    
  82.        else if(packet instanceof jpcap.packet.ARPPacket){    
  83.            ARPPacket p=(ARPPacket)packet;    
  84.            //Returns the hardware address (MAC address) of the sender    
  85.            Object  saa=   p.getSenderHardwareAddress();    
  86.            Object  taa=p.getTargetHardwareAddress();    
  87.            String s="* * * ARPPacket:| SenderHardwareAddress "+saa    
  88.              +"|TargetHardwareAddress "+taa    
  89.              +" |len: "+p.len;    
  90.          System.out.println(s);    
  91.                 
  92.         }    
  93.     //取得鏈路層數據頭 :如果你想局網抓包或僞造數據包,嘿嘿    
  94.      DatalinkPacket datalink  =packet.datalink;    
  95.      //如果是以太網包    
  96.      if(datalink instanceof jpcap.packet.EthernetPacket){    
  97.          EthernetPacket ep=(EthernetPacket)datalink;    
  98.           String s="  datalink layer packet: "    
  99.               +"|DestinationAddress: "+ep.getDestinationAddress()    
  100.               +"|SourceAddress: "+ep.getSourceAddress();    
  101.           System.out.println(s);    
  102.     }        
  103.   }    
  104.     
  105. }     




運行這段程序,你機器從網絡收所有收發到的數據,就都可以展現在你眼前了!,在我的機器上,摘抄一段輸出如下: 
Java代碼  收藏代碼
  1. datalink layer packet: |DestinationAddress: 01:00:5e:00:00:01|SourceAddress: 00:19:e0:f0:ee:55  
  2.   datalink layer packet: |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: 62  datalink layer packet: |DestinationAddress: 00:19:e0:f0:ee:55|SourceAddress: 00:02:8a:96:d1:ab* * * ARPPacket:| SenderHardwareAddress 00:19:e0:f0:ee:55|TargetHardwareAddress 00:00:00:00:00:00 |len: 60  datalink layer packet: |DestinationAddress: ff:ff:ff:ff:ff:ff|SourceAddress: 00:19:e0:f0:ee:55* * * ARPPacket:| SenderHardwareAddress 00:02:8a:96:d1:ab|TargetHardwareAddress 00:19:e0:f0:ee:55 |len: 42  datalink layer packet: |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: 62  datalink layer packet: |DestinationAddress: 00:02:8a:96:d1:ab|SourceAddress: 00:19:e0:f0:ee:55  

注意:你可能認爲要抓取的數據包應分爲發送出去的和接收到的;但對jPcap而言,數據包是從網卡上抓取到的一個packet對象,packet沒有收發的概念;如果你要確定這個packet的方向,則可根據其中的屬性數據值,如源地址、目標地址確認。如果想深入的研究網絡協議就請繼續鑽研吧,我們的任務可僅僅只是統計網絡流量,demo了這麼多,趕快實現我們的數據統計模塊。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章