Listening to network traffic using Java

Using JPCAP to capture packets, you can use JAVA to write code to analyze the data, and finally visualize it;

This article only implements the function of capturing data packets in the IDEA environment using the jpcap interface of the Java language

1. Java builds winpcap development environment

Before capturing packets, you need to build a winpcap development environment in Java;

1. Explanation of terms

1.1 winpcap

winpcap is a more low-level system, through this software, direct network programming can be realized under the window platform;
but it should be noted that the implementation of winpcap is implemented using C/C++, so we need a middleware to realize from C to Java conversion;

1.2 jpcap

Simply speaking, jpcap is a layer of encapsulation for winpcap, calling winpcap, providing a java code interface, so that Java can realize the acquisition of network traffic;

1.3 network card

No matter what operating system we are in, if we want to send datagrams to the network, we cannot do without such a thing:
no matter what data packets are sent from the network to the machine by the network card, they are saved to the local buffer through the network card,
so we are in When capturing packets in network programming, you must first determine which network card to capture packets from

2. Build a development environment

2.1 Install winpcap

Download the installation package

Address: https://www.winpcap.org/

After the completion, it can be installed on the Windows computer, and  the next step in the installation process is OK.

注意: 有的电脑上可能之前已经安装过类似的软件, 会导致安装的时候提示, 无法安装, 此时去到C:\Windows\SysWOW64目录下, 将wpcap.dll 重命名为 wpcap.dll.old, packet.dll 重命名为 packet.dll.old;  一般来说是会把packet.dll改名,  改名之后, 再次点击安装winpcap程序即可

2.2 设置Jpcap.dll

特别注意:64位的系统要下载64位的对应的DLL;

但是官网上提供的下载是32位系统的!!

我在百度网盘上传了一份64位的:

链接:https://pan.baidu.com/s/1bec0NZMQGJtMBrzdbaeYvQ?pwd=nmdf

提取码:nmdf

下载到本地之后,将Jpcap.dll复制到JDK安装路径下的bin 目录下;

2.3 导入jar包

使用IDEA新建一个普通的Java项目,导入Jar包

打开项目的项目结构,将刚才下载Jar包添加到项目结构的库中,如下图:

3. 测试demo

在进行了开发环境的搭建后,就让我们来一段代码尝试监听

3.1 获取本机网卡

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;

public class JpcapDemo {
    public static void main(String[] args) throws IOException {
        /*-------第一步,显示网络设备列表-------- */
        // 获取网络接口列表,返回你所有的网络设备数组,一般就是网卡;
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();

        // 显示所有网络设备的名称和描述信息;
        // 要注意的是,显示出来的网络设备在不同网络环境下是不同的,可以在控制台使用 ipconfig /all命令查看;
        for (int i = 0; i < devices.length; i++) {
            NetworkInterface n = devices[i];
            System.out.println("序号 " + i + "   " + n.name + "     |     " + n.description);
            System.out.println("------------------------------------------------");
        }
    }
}

3.2 网卡分析

显示出来的网络设备在不同网络环境下是不同的,可以在控制台使用 ipconfig /all命令查看,如下图

如果你也可以像上面代码一样显示出网络设备的信息,那就恭喜你搭建网络环境成功啦!接着进行下一步吧!

3.3 监听网卡流量数据

import jpcap.NetworkInterface;
import jpcap.PacketReceiver;
import jpcap.packet.Packet;
import jpcap.packet.TCPPacket;

import java.io.IOException;

public class JpcapSimple {
    public static void main(String[] args) throws IOException {
        /*-------第一步,显示网络设备列表----------------------------------------- */
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();

        /*--------第二步,选择网卡并打开网卡连接------------------------------------*/
        NetworkInterface network_interface = devices[0];

        /*--------第三步,捕获数据包-----------------------------------------------*/
        JpcapCaptor captor = JpcapCaptor.openDevice(network_interface, 65535, false, 20);
        captor.loopPacket(-1, new ReceiverSimple());
    }
}

class ReceiverSimple implements PacketReceiver {

    public void receivePacket(Packet packet)
    {
        if (packet instanceof TCPPacket) {// tcp数据包的解析
            //源ip地址, 目的ip地址
            TCPPacket tcp_packet = (TCPPacket)packet;
            System.out.print("ip地址----");
            System.out.print("源--" +tcp_packet.src_ip);
            System.out.print("   ");
            System.out.println("目的--" + tcp_packet.dst_ip);
            //源端口地址, 目的端口地址
            System.out.print("端口----");
            System.out.print("源--" +tcp_packet.src_port);
            System.out.print("   ");
            System.out.println("目的--" + tcp_packet.dst_port);
        }
        System.out.println();
    }
}

二、连接网卡

1. 获取网络接口列表

要想从网络中捕获数据包,第一件必须要做的事就是获取本机的网络接口列表

Jpcap提供了方法JpcapCaptor.getDeviceList()完成这个任务,该方法返回一组NetworkInterface对象

NetworkInterface接口对象包含了对应网络接口的一些信息,例如:名称、描述、IP以及MAC地址以及数据链路层名称和描述

获取网络接口的代码就是上面的JpcapDemo代码,大家可以回头看看,为了节省篇幅这里就不贴啦

2. 打开网络接口

一旦有了网络接口列表就可以从选定用于捕获数据包的网络接口,可以使用方法JpcapCaptor.openDevice()来打开指定的网络接口,注意此并未开始捕获数据包。

2.1 代码实现

代码如下:

       /*-------第一步,显示网络设备列表----------------------------------------- */
        // 获取网络接口列表,返回你所有的网络设备数组,一般就是网卡;
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();

        /*--------第二步,选择网卡并打开网卡连接------------------------------------*/
        // 选择网卡序号;
        // 注意!每台设备连接网络的网卡不同,选择正确的网卡才能捕获到数据包;
        NetworkInterface network_interface = devices[0];

        // 打开网卡连接,此时还未开始捕获数据包;
        // 参数一:选择一个网卡,调用 JpcapCaptor.openDevice()连接,返回一个 JpcapCaptor类的对象 jpcap;
        // 参数二:限制每一次收到一个数据包,只提取该数据包中前65535个字节;
        // 参数三:设置为非混杂模式,才可以使用下面的捕获过滤器方法;
        // 参数四:指定超时的时间;
        JpcapCaptor captor = JpcapCaptor.openDevice(network_interface, 65535, false, 20);

        /*--------第三步,捕获数据包-----------------------------------------------*/
        //第一个参数为需要捕获的IP包个数,-1表示一直捕获。
        //第二个参数表示需要注册的处理器。
        captor.loopPacket(-1, new Receiver());

2.2 方法解释

静态方法 static JpcapCaptor openDevice (NetworkInterface interface, int snaplen, boolean promisc, int to_ms):
创建一个与指定设备的连接并返回该连接。调用该方法必须指定下列参数:
interface:要打开连接的设备的实例;
snaplen:这个是比较容易搞混的一个参数。其实这个参数不是限制只能捕捉多少数据包,而是限制每一次收到一个数据包,只提取该数据包中前多少字节;
promisc:设置是否混杂模式。处于混杂模式将接收所有数据包,若之后又调用了包过滤函数 setFilter() 将不起任何作用 (后面会介绍);
to_ms:指定捕获数据包超时的时间;这个参数主要用于 processPacket()方法 (后面会介绍);

2. 回调(callback)

2.1 实现的细节:

(1) 首先定义一个实现 PacketReceiver接口的类;

  • PacketReceiver 接口中只定义了一个 receivePacket()方法;

Void receivePacket (Packet p):
* 实现类中的处理接收到的 Packet对象的方法。每个Packet对象代表从热指定网络接口上抓取到的数据包;
* 开始接收数据包后,当接收到数据包时就会回调实现 PacketReceiver接口的类的 receivePacket的方法,使之处理接收到的数据包;

(2) 然后在主方法中调用 processPacket() 方法捕获数据包;

processPacket()或 loopPacket()方法可以指定捕获的数据包的数量,两种方法非常相似;

通常建议使用 processPacket(),因为它支持超时以及非阻塞模式,而 loopPacket()并不支持,不受 to_ms参数影响;

int processPacket(int count, PacketReceiver handler):
* 参数1: 解释 一次接收包的个数(个数到时到产生回调)捕捉指定数目的数据包,并交由实现了 PacketReceiver接口的类的实例(第二个参数)处理; 如果设置为 -1,则表示永远抓下去—方法不会返回;
* 参数2: 解释 (回调者)事件临听者,必须是实现了 PacketReceiver接口的一个实例对象,抓到的包将调用这个对象中的 receivePacket(Packet packet)方法处理;

注意! processPacket()方法收到to_ms 参数的影响,超时会结束该方法;

2.2 捕获数据包

由于使用了新的抓包方法,所以重新贴了一个全部的实现代码,过程和上面是一样的;

*部分代码如下,为节省篇幅

import ...

// 类 Receiver实现了 PacketReceiver接口的 receivePacket()方法;
class Receiver implements PacketReceiver {
    @Override
    // 重写 PacketReceiver接口中的 receivePacket()方法;
    // 实现类中的处理接收到的 Packet 对象的方法,每个 Packet对象代表从指定网络接口上抓取到的数据包;
    // 抓到的包将调用这个 PacketReceiver对象中的 receivePacket(Packet packet)方法处理;
    public void receivePacket(Packet packet) {
        System.out.println(packet);// 直接将捕获的包输出,不做任何处理;
    }
}
// 主方法;
public class JpcapProcess {
    public static void main(String[] args) {

        ......

        //第二步,监听选中的网卡;
        try {
            // 参数一:选择一个网卡,调用 JpcapCaptor.openDevice()连接,返回一个 JpcapCaptor类的对象 jpcap;
            // 参数二:限制每一次收到一个数据包,只提取该数据包中前1512个字节;
            // 参数三:设置为非混杂模式,才可以使用下面的捕获过滤器方法;
            // 参数四:指定超时的时间;

            JpcapCaptor jpcap = JpcapCaptor.openDevice(devices[index], 1512, true, 6000);

        //第三步,捕获数据包;
            /* 调用 processPacket()方法, count = -1对该方法无影响,主要受 to_ms控制,
                改成其他数值则会控制每一次捕获包的数目;*/
            // 换而言之,影响 processPacket()方法的因素有且只有两个,分别是count 和 to_ms;
            // 抓到的包将调用这个 new Receiver()对象中的 receivePacket(Packet packet)方法处理;
            jpcap.loopPacket(-1, new Receiver());
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("抓取数据包时出现异常!!");
        }
    }
}

三. 将监听数据发送到kafka中

  1. 添加依赖

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka_2.12</artifactId>
            <version>3.2.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.3</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

2. 封装流量数据的实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class WuYuan {

    private String srcIp;
    private Integer srcPort;
    private String dstIp;
    private Integer dstPort;


}

3.重写Receiver

class ReceiverSimple implements PacketReceiver {

    public KafkaProducer kafkaProducer;
    public ObjectMapper objectMapper = new ObjectMapper();

    public ReceiverSimple(){
//        0. 配置文件
        Properties properties = new Properties();
        properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "81.70.199.213:9092");
        properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

//        1. KafkaProducer对象  执行者-数据发送的执行者
        this.kafkaProducer = new KafkaProducer(properties);
    }


    //数据包  网络包  包
    public void receivePacket(Packet packet)
    {
        if (packet instanceof TCPPacket) {// tcp数据包的解析
            TCPPacket tcp_packet = (TCPPacket)packet;
            WuYuan wuYuan = new WuYuan(tcp_packet.src_ip.getHostAddress(), tcp_packet.src_port, tcp_packet.dst_ip.getHostAddress(), tcp_packet.dst_port);
            try {
                String jsonWuYuan = objectMapper.writeValueAsString(wuYuan);

                System.out.println(jsonWuYuan);

                kafkaProducer.send(new ProducerRecord<>("test-topic", jsonWuYuan));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

4.kafka控制台测试数据是否接收到

运行流量捕获程序后, 在kafka控制台中运行命令

./kafka-console-consumer.sh --bootstrap-server 81.70.199.213:9092 --from-beginning --topic test

控制台显示成功接收数据

Guess you like

Origin blog.csdn.net/2301_76154806/article/details/128683691