zoukankan      html  css  js  c++  java
  • 使用Pcap4j实现一个抓包工具

    Troubleshooting是我平时工作中的重要内容,我几乎每天都会花一些时间在定位客户环境的问题上,有很多的问题都需要通过抓包来协助分析,比如定位SSL handshake失败,SNMP请求没响应的问题等。Linux平台一般使用tcpdump抓包,由于我们只能通过远程脚本调用的方式执行,所以对windows我没法使用wireshark之类的GUI工具,所以一般用netsh( 参考资料3)进行抓包。但是,linux上有时候并没有安装tcpdump或者登录的用户没有权限执行tcpdump,而且windows上使用netsh抓包很麻烦,而且过滤方式很弱,所以,我们就想着能不能有其他的抓包方式。

    Pcap4j刚好满足我们的要求,下面接单介绍下使用pcap4j如何实现抓包。

    创建项目

    创建一个maven项目,设置完项目后,将pcap4j的依赖加到pom.xml中:

    <!--pom.xml-->
    <!--pcap4j imports-->
    <dependency>
        <groupId>org.pcap4j</groupId>
        <artifactId>pcap4j-core</artifactId>
        <version>1.7.3</version>
    </dependency>
    <dependency>
        <groupId>org.pcap4j</groupId>
        <artifactId>pcap4j-packetfactory-static</artifactId>
        <version>1.7.3</version>
    </dependency>
    

    打开网卡抓第一个包

    Pcap4j提供了一个很好的工具类org.pcap4j.core.Pcaps,可以方便的根据名字获得一个网卡。拿到网卡后,打开一个PcapHandle,并获取第一个包并打印包信息。

    // 根据网卡名获取网卡
    PcapNetworkInterface nif = Pcaps.getDevByName(name);
    int snapLen = 65536;
    PromiscuousMode mode = PromiscuousMode.PROMISCUOUS;
    int timeout = 10;
    // 打开一个句柄
    PcapHandle handle = nif.openLive(snapLen, mode, timeout);
    // 获取一个包
    Packet packet = handle.getNextPacketEx();
    handle.close();
    System.out.println(packet);
    

    可以通过ifconfig先查下网卡名字。Pcap也可以列出所有的网卡,然后自己过滤:

    List<PcapNetworkInterface> inters = Pcaps.findAllDevs();
    // select you interface
    

    抓指定数量的包

    通常,我们抓包的时候并不是抓一个包就够了。Pcap提供了一个抽象的方法,可以连续抓多个包。

    // Create a listener to handle the packets
    PcapNetworkInterface nif = Pcaps.getDevByName(name);
    int snapLen = 65536;
    PromiscuousMode mode = PromiscuousMode.PROMISCUOUS;
    int timeout = 10;
    // 打开一个句柄
    PcapHandle handle = nif.openLive(snapLen, mode, timeout);
    
    // 自定义一个Packet的listener处理抓到的包
    PacketListener listener = new PacketListener() {
        @Override
        public void gotPacket(Packet packet) {
            System.out.println(handle.getTimestamp());
            System.out.println(packet);
        }
    };
    
    // 让handle使用创建的listener,且指定抓50个包
    try {
        int maxPackets = 50;
        handle.loop(maxPackets, listener);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
    handle.close();
    

    设置filter

    就像使用tcpdump一样,我们不希望把所有的包都抓到,所以我们会在运行tcpdump的时候指定一个filter。同样,Pcap4j也支持一样的filter语法:

    // 打开网卡
    .....
    // 打开handle
    final PcapHandle handle = device.openLive(SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT);
    
    // 设置filter过滤经过443端口的TCP包
    String filter = "tcp port 443";
    handle.setFilter(filter, BpfCompileMode.OPTIMIZE);
    
    // 其他代码。。。
    
    

    写到PCAP文件

    通常抓包是在产品环境或者客户环境上,我们没法直接对抓到的包处理,常用的方法是保存到文件,然后下载下来用wireshark打开再分析。Pcap4j可以很方便的把抓到的包保存到pcap文件中。

    ....
    // 获取网卡并打开handle
    ....
    // 通过handle创建一个dumper
    PcapDumper dumper = handle.dumpOpen("capturedPackets.pcap");
    // ....抓包....
    // 处理包的时候保存到文件
    try {
        dumper.dump(packet, handle.getTimestamp());
    } catch (NotOpenException e) {
        e.printStackTrace();
    }
    
    // 关闭句柄
    dumper.close();
    
    

    下面是完整的实例代码:

    package cc.databus.netool;
    
    import com.sun.jna.Platform;
    import org.pcap4j.core.*;
    import org.pcap4j.packet.Packet;
    
    import java.io.EOFException;
    import java.util.concurrent.TimeoutException;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class DumpPacketsDemo {
        public static void main(String[] args) throws PcapNativeException, EOFException, TimeoutException, NotOpenException {
            if (args.length < 2) {
                System.err.println("netool <ethName> <count>");
                return;
            }
    
            String nifName = args[0];
            int count = Integer.parseInt(args[1]);
    
            // 1. get network interface
            PcapNetworkInterface nif = Pcaps.getDevByName(nifName);
            if (nif == null)  {
                System.err.println("Cannot get interfance - " + nifName);
                return;
            }
            // 2. open handle
            PcapNetworkInterface.PromiscuousMode mode = PcapNetworkInterface.PromiscuousMode.PROMISCUOUS;
            int timeout = 10;
            int snapLen = 65536;
            PcapHandle handle = nif.openLive(snapLen, mode, timeout);
    
            // 4. set pcap dumper
            final PcapDumper dumper = handle.dumpOpen("dump.pcap");
    
            final AtomicLong dumped = new AtomicLong(0);
            try {
                // 5. set filter
                handle.setFilter("tcp port 443", BpfProgram.BpfCompileMode.OPTIMIZE);
    
                // 6. prepare listener
                PacketListener listener = new PacketListener() {
                    @Override
                    public void gotPacket(Packet packet) {
                        try {
                            dumper.dump(packet);
                            dumped.incrementAndGet();
                        }
                        catch (NotOpenException ignore) {
                        }
                    }
                };
    
                // 7. start looper
                try {
                    handle.loop(count, listener);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                // Print out handle statistics
                PcapStat stats = handle.getStats();
                System.out.println("Pakcets dumped: " + dumped.get());
                System.out.println("Packets received: " + stats.getNumPacketsReceived());
                System.out.println("Packets dropped: " + stats.getNumPacketsDropped());
                System.out.println("Packets dropped by interface: " + stats.getNumPacketsDroppedByIf());
                // Supported by WinPcap only
                if (Platform.isWindows()) {
                    System.out.println("Packets captured: " +stats.getNumPacketsCaptured());
                }
            }
            finally {
                dumper.close();
                handle.close();
            }
        }
    }
    
    

    参考文献

    1. Pcap4j官网
    2. 基于Pcap4j实现的抓包工具
    3. Windows使用netsh抓包

    文章同步发布在我的个人博客https://jianyuan.me上,欢迎拍砖。
    传送门: 使用Pcap4j实现一个抓包工具

    个人博客地址: https://jianyuan.me
  • 相关阅读:
    POJ-3131-Cubic Eight-Puzzle(双向BFS+哈希)
    Android WebView与JavaScript交互操作(Demo)
    程序猿Web面试之jQuery
    Java数据类型(基本数据类型和引用数据类型)
    Swift学习——A Swift Tour 枚举和结构体
    大龙的学习笔记之“虚方法,抽象方法,重写,抽象类,接口”
    thinkphp curd的事务回滚 一看就会
    图书源代码下载: Modern Differential Geometry of CURVES and SURFACES with Mathematica
    UOJ#422. 【集训队作业2018】小Z的礼物
    删除所选项(附加搜索部分的jquery)
  • 原文地址:https://www.cnblogs.com/yflog/p/9523514.html
Copyright © 2011-2022 走看看