Java實現(xiàn)抓包程序(網(wǎng)絡協(xié)議分析程序)

前言

本學期計算機網(wǎng)絡要求寫一個抓包程序,我通過網(wǎng)上查閱資料,如何實現(xiàn)抓包,實現(xiàn)了一個較為簡單的抓包程序。

項目準備

1.首先得有java編譯環(huán)境,安裝并配置好jdk;
2.需要安裝Winpcap,Winpcap是windows平臺下的一個免費的,公共的網(wǎng)絡訪問系統(tǒng)(Linux系統(tǒng)是Libpcap);
3.還需要下載Jpcap,Jpcap就是調(diào)用Winpcap給java提供一個公共的接口,從而實現(xiàn)平臺無關(guān)性,并能夠捕獲發(fā)送數(shù)據(jù)包。Jpcap包括Jpcap.jar和Jpcap.dll,兩者需要版本一致,并且區(qū)分32位和64位。將Jpcap.jar導入你的idea或者Eclipse項目,并且把Jpcap.dll復制到java的jdk的bin目錄下,就ok了。

注:我的項目是用idea開發(fā)的。

一、抓包功能的基本實現(xiàn)

前面的準備工作完成后,我們就可以使用Jpcap編程進行ip數(shù)據(jù)包的捕獲了。

  • JpcapHandler :這個接口用來定義分析被捕獲數(shù)據(jù)包的方法;

  • ARPPacket:這個類描述了ARP/RARP包,繼承了Packet類;

  • DatalinkPacket :這個抽象類描述了數(shù)據(jù)鏈路層;

  • EthernetPacket :這個類描述了以太幀包,繼承DatalinkPacket類;

  • ICMPPacket:這個類描述了ICMP包,繼承了IPPacket類;

  • IPAddress:這個類描述了IPv4和IPv6地址,其中也包含了將IP地址轉(zhuǎn)換為域名的方法;

  • IPPacket:這個類描述了IP包,繼承了Packet類,支持IPv4和IPv6;

  • IPv6Option :這個類描述了IPv6選項報頭;

  • Jpcap:用來捕獲數(shù)據(jù)包;

  • Jpcap.JpcapInfo :Jpcap的內(nèi)部類,它包含被捕獲數(shù)據(jù)包的信息(在jpcap0.4修改部分BUG之后不再使用這個類);

  • JpcapSender :它用來發(fā)送一個數(shù)據(jù)包;

  • JpcapWriter :它用來將一個被捕獲的數(shù)據(jù)包保存到文件;

  • Packet :這個類是所有被捕獲的數(shù)據(jù)包的基類;

  • TCPPacket:這個類描述TCP包,繼承了IPPacket類;

  • UDPPacket :這個類描述了UDP包,繼承了IPPacket類;

    以抓取ip數(shù)據(jù)包為例,JPCAP抓包基本步驟為:綁定網(wǎng)絡設備、抓包、分析。
    以下附上基本功能實現(xiàn)的代碼(無界面,能夠基本實現(xiàn)抓包功能):

import java.io.IOException;
 
import jpcap.*;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
 
public class JpcapPacket {
    public static void main(String[] args)
    {
        /*--------------    第一步綁定網(wǎng)絡設備       --------------*/ 
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();
        
        for(NetworkInterface n : devices)
        {
            System.out.println(n.name + "     |     " + n.description);
        }
        System.out.println("-------------------------------------------");
        
        JpcapCaptor jpcap = null;
        int caplen = 1512;
        boolean promiscCheck = true;
        
        /*
        devices[ ]中的數(shù)字需要注意,這里的數(shù)字根據(jù)你的網(wǎng)卡而定,
        你選擇抓包的網(wǎng)卡正確才能抓到數(shù)據(jù)包,
        不同設備在使用有線網(wǎng)和無線網(wǎng)時的都不一樣,
        具體的需要自己去試驗。
        */
        try{
            jpcap = JpcapCaptor.openDevice(devices[1], caplen, promiscCheck, 50);
        }catch(IOException e)
        {
            e.printStackTrace();
        }
        
        /*----------第二步抓包-----------------*/
        int i = 0;
        while(i < 10)
        {
            Packet packet  = jpcap.getPacket();
            if(packet instanceof IPPacket && ((IPPacket)packet).version == 4)
            {
                i++;
                IPPacket ip = (IPPacket)packet;//強轉(zhuǎn)
                
                System.out.println("版本:IPv4");
                System.out.println("優(yōu)先權(quán):" + ip.priority);
                System.out.println("區(qū)分服務:最大的吞吐量: " + ip.t_flag);
                System.out.println("區(qū)分服務:最高的可靠性:" + ip.r_flag);
                System.out.println("長度:" + ip.length);
                System.out.println("標識:" + ip.ident);
                System.out.println("DF:Don't Fragment: " + ip.dont_frag);
                System.out.println("NF:Nore Fragment: " + ip.more_frag);
                System.out.println("片偏移:" + ip.offset);
                System.out.println("生存時間:"+ ip.hop_limit);
                
                String protocol ="";
                switch(new Integer(ip.protocol))
                {
                case 1:protocol = "ICMP";break;
                case 2:protocol = "IGMP";break;
                case 6:protocol = "TCP";break;
                case 8:protocol = "EGP";break;
                case 9:protocol = "IGP";break;
                case 17:protocol = "UDP";break;
                case 41:protocol = "IPv6";break;
                case 89:protocol = "OSPF";break;
                default : break;
                }
                System.out.println("協(xié)議:" + protocol);
                System.out.println("源IP " + ip.src_ip.getHostAddress());
                System.out.println("目的IP " + ip.dst_ip.getHostAddress());
                System.out.println("源主機名: " + ip.src_ip);
                System.out.println("目的主機名: " + ip.dst_ip);
                System.out.println("----------------------------------------------");
            }
        }
        
    }
}

二、完整項目實現(xiàn)

具有界面,能夠?qū)崿F(xiàn)基本功能(查看網(wǎng)卡信息,開始抓包,暫停抓包(開始抓包后“開始”按鈕變?yōu)椤皶和!保蹇战缑鎯?nèi)容,退出,以及過濾器功能的簡單實現(xiàn))),界面如下圖:

界面如圖

注:界面使用swing實現(xiàn)(現(xiàn)在swing基本很少用了,不過做個簡單界面還是不錯)

1.界面布局

JpCapFrame代碼如下:

package packetCapture;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import java.awt.*;

/**
 * 流式布局
 */
public class JpCapFrame extends JFrame {
    private static DefaultTableModel model;
    private static JTextField filterField;
    private JTextArea showArea;
    private JButton startBtn;
    private JButton checkBtn;
    private JButton exitBtn;
    private JButton clearBtn;

    public JpCapFrame() {
        super();
        initGUI();
    }

    public static DefaultTableModel getModel() {
        return model;
    }

    public JTextArea getShowArea() {
        return showArea;
    }

    public JButton getStartBtn() {
        return startBtn;
    }

    public JButton getCheckBtn() {
        return checkBtn;
    }

    public JButton getExitBtn() {
        return exitBtn;
    }

    public JButton getClearBtn() {
        return clearBtn;
    }

    public static JTextField getFilterField() {
        return filterField;
    }

    private void initGUI() {
        Font font1 = new Font("宋體", Font.BOLD, 15);
        Font font4 = new Font("宋體", Font.BOLD, 14);
        Font font2 = new Font("宋體", Font.PLAIN, 16);
        Font font3 = new Font("微軟雅黑", Font.PLAIN, 16);

        //界面
        setSize(1550, 1000);
        setVisible(true);
        setTitle("Captor");
        Container container = this.getContentPane();

        //頂部
        JPanel pane = new JPanel();
        pane.setBounds(0, 0, 775, 150);
        pane.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0));
        pane.setPreferredSize(new Dimension(775, 27));

        checkBtn = new JButton("查看網(wǎng)卡信息");
        checkBtn.setFont(font4);
        checkBtn.setBounds(0, 0, 50, 0);
        pane.add(checkBtn);

        startBtn = new JButton("開始");
        startBtn.setFont(font4);
        startBtn.setBounds(0, 0, 50, 0);
        pane.add(startBtn);

        clearBtn = new JButton("清空");
        clearBtn.setFont(font4);
        clearBtn.setBounds(0, 0, 50, 0);
        pane.add(clearBtn);

        exitBtn = new JButton("退出");
        exitBtn.setFont(font4);
        exitBtn.setBounds(0, 0, 50, 0);
        pane.add(exitBtn);

        JPanel panelTest = new JPanel();
        panelTest.setBounds(775, 0, 775, 150);
        panelTest.setPreferredSize(new Dimension(775, 27));
        panelTest.setLayout(new FlowLayout(FlowLayout.RIGHT, 20, 0));

        JLabel filter = new JLabel("Filter:");
        filter.setFont(font1);
        filter.setBounds(0, 0, 500, 0);
        filterField = new JTextField(50);
        filterField.setBounds(200, 0, 500, 0);
        panelTest.add(filter);
        panelTest.add(filterField);

        //中部主體內(nèi)容顯示區(qū)
        String[] name = {"No.", "Time", "Source", "Destination", "Protocol", "Length", "Info"};
        //model = new DefaultTableModel();
        //model.setColumnIdentifiers(name);
        JTable table = new JTable(model);
        JTableHeader tableHeader = table.getTableHeader();
        tableHeader.setFont(font1);
        table.setFont(font2);
        table.setRowHeight(20);
        model = (DefaultTableModel) table.getModel();
        model.setColumnIdentifiers(name);
        table.setEnabled(false);
        JScrollPane jScrollPane = new JScrollPane(table);
        jScrollPane.setBounds(0, 300, 1550, 600);

        //底部
        JPanel pane2 = new JPanel();
        pane2.setLayout(new BorderLayout());
        pane2.setPreferredSize(new Dimension(1550, 300));

        showArea = new JTextArea(5, 5);
        //showArea.setBounds(0,0,1200,300);
        //showArea.setText("Test");
        showArea.setEditable(false);
        showArea.setLineWrap(false);
        showArea.setFont(font3);
        //showArea.setBackground(Color.GRAY);
        //pane2.add(showArea);
        pane2.setSize(10, 10);
        pane2.setBounds(0, 0, 1, 1);
        //給textArea添加滾動條
        JScrollPane scrollPane = new JScrollPane(showArea);
        scrollPane.setBounds(0, 0, 1, 1);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        pane2.add(scrollPane, BorderLayout.CENTER);
        scrollPane.setViewportView(showArea);

        container.add(jScrollPane, BorderLayout.CENTER);
        container.add(pane, BorderLayout.NORTH);
        container.add(panelTest, BorderLayout.NORTH);
        container.add(pane2, BorderLayout.SOUTH);

        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

2.抓包功能管理類

JpCapPacket代碼如下:

package packetCapture;

import jpcap.JpcapCaptor;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;

import java.sql.Timestamp;
import java.util.Vector;

public class JpCapPacket {
    private JpcapCaptor jpcap;

    public JpCapPacket(JpcapCaptor jpcap) {
        this.jpcap = jpcap;
    }

    void capture() throws InterruptedException {
        int i = 0;
        while (true) {
            synchronized (JpCapMain.getThread()) {
                if (JpCapMain.isPause()) {
                    JpCapMain.getThread().wait();
                }
            }
            Packet packet = jpcap.getPacket();
            if (packet instanceof IPPacket && ((IPPacket) packet).version == 4) {
                i++;
                IPPacket ip = (IPPacket) packet;//強轉(zhuǎn)

//                System.out.println("版本:IPv4");
//                System.out.println("優(yōu)先權(quán):" + ip.priority);
//                System.out.println("區(qū)分服務:最大的吞吐量: " + ip.t_flag);
//                System.out.println("區(qū)分服務:最高的可靠性:" + ip.r_flag);
//                System.out.println("長度:" + ip.length);
//                System.out.println("標識:" + ip.ident);
//                System.out.println("DF:Don't Fragment: " + ip.dont_frag);
//                System.out.println("NF:Nore Fragment: " + ip.more_frag);
//                System.out.println("片偏移:" + ip.offset);
//                System.out.println("生存時間:" + ip.hop_limit);

                String protocol = "";
                switch (new Integer(ip.protocol)) {
                    case 1:
                        protocol = "ICMP";
                        break;
                    case 2:
                        protocol = "IGMP";
                        break;
                    case 6:
                        protocol = "TCP";
                        break;
                    case 8:
                        protocol = "EGP";
                        break;
                    case 9:
                        protocol = "IGP";
                        break;
                    case 17:
                        protocol = "UDP";
                        break;
                    case 41:
                        protocol = "IPv6";
                        break;
                    case 89:
                        protocol = "OSPF";
                        break;
                    default:
                        break;
                }
//                System.out.println("協(xié)議:" + protocol);
//                System.out.println("源IP " + ip.src_ip.getHostAddress());
//                System.out.println("目的IP " + ip.dst_ip.getHostAddress());
//                System.out.println("源主機名: " + ip.src_ip);
//                System.out.println("目的主機名: " + ip.dst_ip);
//                System.out.println("----------------------------------------------");
                String filterInput = JpCapFrame.getFilterField().getText();
                if (filterInput.equals(ip.src_ip.getHostAddress()) ||
                        filterInput.equals(ip.dst_ip.getHostAddress()) ||
                        filterInput.equals(protocol) ||
                        filterInput.equals("")) {
                    Vector dataVector = new Vector();
                    Timestamp timestamp = new Timestamp((packet.sec * 1000) + (packet.usec / 1000));

                    dataVector.addElement(i + "");
                    //dataVector.addElement(new SimpleDateFormat("yyyy年MM月dd日HH時mm分ss").format(new Date()));
                    dataVector.addElement(timestamp.toString());//數(shù)據(jù)包時間
                    dataVector.addElement(ip.src_ip.getHostAddress());
                    dataVector.addElement(ip.dst_ip.getHostAddress());
                    dataVector.addElement(protocol);
                    dataVector.addElement(packet.data.length);

                    String strtmp = "";
                    for (int j = 0; j < packet.data.length; j++) {
                        strtmp += Byte.toString(packet.data[j]);
                    }
                    dataVector.addElement(strtmp); //數(shù)據(jù)內(nèi)容

                    JpCapFrame.getModel().addRow(dataVector);
                }
            }
        }
    }
}

3.主界面及功能實現(xiàn)

JpCapMain代碼如下:

package packetCapture;

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;

import java.io.IOException;

public class JpCapMain implements Runnable {
    JpCapFrame frame;
    JpcapCaptor jpcap = null;
    private static Thread thread = null;
    private static boolean pause = true;

    public JpCapMain() {
        //創(chuàng)建界面
        frame = new JpCapFrame();
        frame.setVisible(true);

        //綁定網(wǎng)絡設備
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();

        int caplen = 1512;
        boolean promiscCheck = true;

        /*
        WIFI:3
        有線:1
        (不同設備對應的不一樣)
        */
        int device = 1;
        try {
            jpcap = JpcapCaptor.openDevice(devices[device], caplen, promiscCheck, 50);
        } catch (IOException e) {
            e.printStackTrace();
        }

        frame.getCheckBtn().addActionListener(e -> {
            frame.getShowArea().append("當前設備全部網(wǎng)絡設備信息為: \n");

            for (NetworkInterface n : devices) {
                System.out.println(n.name + "     |     " + n.description);
                frame.getShowArea().append(n.name + "     |     " + n.description + "\n");
            }
            //System.out.println("-------------------------------------------");
            frame.getShowArea().append(printSeparator(110, 0));
            frame.getShowArea().append("\n當前使用網(wǎng)卡信息: " + devices[device].name + "     |     " + devices[device].description + "\n");
            frame.getShowArea().append(printSeparator(110, 1));
        });

        frame.getStartBtn().addActionListener(e -> {
            if (pause) {
                if (thread == null) {
                    frame.getShowArea().append("   開始抓包,抓取范圍為:" + JpCapFrame.getFilterField().getText() + " ……\n");
                    thread = new Thread(this);
                    thread.setPriority(Thread.MIN_PRIORITY);
                    //thread.sleep(100);
                    thread.start();
                    pause = false;
                    frame.getStartBtn().setText("暫停");
                } else {
                    frame.getStartBtn().setText("暫停");
                    pause = false;
                    frame.getShowArea().append("   繼續(xù)抓包,抓取范圍為:" + JpCapFrame.getFilterField().getText() + " ……\n");
                    synchronized (thread) {
                        thread.notify();
                    }
                }
            } else {
                pause = true;
                frame.getStartBtn().setText("開始");
                frame.getShowArea().append("    暫停抓包\n");
            }
        });

        frame.getClearBtn().addActionListener(e -> {
            frame.getShowArea().setText("");
            frame.getModel().setRowCount(0);
        });

        frame.getExitBtn().addActionListener(e -> {
            System.exit(0);
        });
    }

    public static void main(String[] args) {
        new JpCapMain();
    }

    @Override
    public void run() {
        try {
            new JpCapPacket(jpcap).capture();
            thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param separator "-"的數(shù)量
     * @param line      "\n"的數(shù)量
     * @return
     */
    public String printSeparator(int separator, int line) {
        String s = "";
        String l = "";

        for (int i = 0; i < separator; i++) {
            s += "-";
        }

        for (int i = 0; i < line; i++) {
            l += "\n";
        }
        return s + l;
    }

    public static Thread getThread() {
        return thread;
    }

    public static boolean isPause() {
        return pause;
    }
}

項目完整代碼如上。

總結(jié)

本項目基本實現(xiàn)了抓包的功能,但是因為做的比較趕,所以還有很多功能沒有完善。比如演示的時候發(fā)現(xiàn)有的同學有選擇網(wǎng)卡的功能,我這里只做了查看網(wǎng)卡的功能,但是實現(xiàn)這個功能還是不難的,就是在devices[device]中想辦法能夠通過選擇網(wǎng)卡與device(int)值對應。另外存在的問題就是界面比較簡單,不是很美觀,然后沒有實現(xiàn)點開每一個數(shù)據(jù)包能夠查看具體信息的功能,均還有待完善。

CSDN文章鏈接:https://blog.csdn.net/twj1248445531/article/details/110926300(這是我CSDN里寫的,有目錄,簡書好像不行,不支持網(wǎng)頁標簽)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容