Java 实现抓包程序

前言

本学期计算机网络要求写一个抓包程序,我通过网上查阅资料,如何实现抓包,实现了一个较为简单的抓包程序。

项目准备

1. 首先得有 java 编译环境,安装并配置好 jdk;2. 需要安装 Winpcap,Winpcap 是 windows 平台下的一个免费的,公共的网络访问系统(Linux 系统是 Libpcap);3. 还需要下载 Jpcap,Jpcap 就是调用 Winpcap 给 java 提供一个公共的接口,从而实现平台无关性,并能够捕获发送数据包。Jpcap 包括 Jpcap.jar 和 Jpcap.dll,两者需要版本一致,并且区分 32 位和 64 位。将 Jpcap.jar 导入你的 idea 或者 Eclipse 项目,并且把 Jpcap.dll 复制到 java 的 jdk 的 bin 目录下,就 ok 了。

注:我的项目是用 idea 开发的。

一、抓包功能的基本实现

前面的准备工作完成后,我们就可以使用 Jpcap 编程进行 ip 数据包的捕获了。

  • JpcapHandler :这个接口用来定义分析被捕获数据包的方法;

  • ARPPacket:这个类描述了 ARP/RARP 包,继承了 Packet 类;

  • DatalinkPacket :这个抽象类描述了数据链路层;

  • EthernetPacket :这个类描述了以太帧包,继承 DatalinkPacket 类;

  • ICMPPacket:这个类描述了 ICMP 包,继承了 IPPacket 类;

  • IPAddress:这个类描述了 IPv4 和 IPv6 地址,其中也包含了将 IP 地址转换为域名的方法;

  • IPPacket:这个类描述了 IP 包,继承了 Packet 类,支持 IPv4 和 IPv6;

  • IPv6Option :这个类描述了 IPv6 选项报头;

  • Jpcap:用来捕获数据包;

  • Jpcap.JpcapInfo :Jpcap 的内部类,它包含被捕获数据包的信息 (在 jpcap0.4 修改部分 BUG 之后不再使用这个类);

  • JpcapSender :它用来发送一个数据包;

  • JpcapWriter :它用来将一个被捕获的数据包保存到文件;

  • Packet :这个类是所有被捕获的数据包的基类;

  • TCPPacket:这个类描述 TCP 包,继承了 IPPacket 类;

  • UDPPacket :这个类描述了 UDP 包,继承了 IPPacket 类;

    以抓取 ip 数据包为例,JPCAP 抓包基本步骤为:绑定网络设备、抓包、分析。以下附上基本功能实现的代码(无界面,能够基本实现抓包功能):

import java.io.IOException;

import jpcap.*;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;

public class JpcapPacket {
 public static void main(String[] args)
 {
        /*-------------- 第一步绑定网络设备       --------------*/ 
  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[ ]中的数字需要注意,这里的数字根据你的网卡而定,
  你选择抓包的网卡正确才能抓到数据包,
  不同设备在使用有线网和无线网时的都不一样,
  具体的需要自己去试验。
  */
  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;//强转

    System.out.println("版本:IPv4");
    System.out.println("优先权:" + ip.priority);
    System.out.println("区分服务:最大的吞吐量: " + ip.t_flag);
    System.out.println("区分服务:最高的可靠性:" + 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("协议:" + 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("----------------------------------------------");
   }
  }

 }
}

二、完整项目实现

具有界面,能够实现基本功能(查看网卡信息,开始抓包,暂停抓包(开始抓包后 “开始” 按钮变为“暂停”,清空界面内容,退出,以及过滤器功能的简单实现)),界面如下图:cb74db65dc444e00ff2a08174d6d0600.png注:界面使用 swing 实现(现在 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("查看网卡信息");
        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);

        //中部主体内容显示区
        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;//强转

//                System.out.println("版本:IPv4");
//                System.out.println("优先权:" + ip.priority);
//                System.out.println("区分服务:最大的吞吐量: " + ip.t_flag);
//                System.out.println("区分服务:最高的可靠性:" + 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("协议:" + 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());//数据包时间
                    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); //数据内容

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

3. 主界面及功能实现

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() {
        //创建界面
        frame = new JpCapFrame();
        frame.setVisible(true);

        //绑定网络设备
        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("当前设备全部网络设备信息为: \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当前使用网卡信息: " + 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("   继续抓包,抓取范围为:" + 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 "-"的数量
     * @param line      "\n"的数量
     * @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;
    }
}

项目完整代码如上。

总结

本项目基本实现了抓包的功能,但是因为做的比较赶,所以还有很多功能没有完善。比如演示的时候发现有的同学有选择网卡的功能,我这里只做了查看网卡的功能,但是实现这个功能还是不难的,就是在 devices[device] 中想办法能够通过选择网卡与 device(int) 值对应。另外存在的问题就是界面比较简单,不是很美观,然后没有实现点开每一个数据包能够查看具体信息的功能,均还有待完善。


作者:主旋律T

来源链接:

https://blog.csdn.net/twj1248445531/article/details/110926300

023ebb4607443fd31723a6df5398a2a9.png

  • 8
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值