Pcap4J抓包基本使用方法

说明:

之前的项目需要一个Windows和Linux平台都能用的抓包模块,搜了之后发现jnetpcap、jpcap都已经很久没有更代码了,而且跨平台支持不好,所以考虑用Pcap4J。

Pcap4J和jNetPcap相同之处,都是基于libpcap/winpcap的。所以于是乎,过滤器规则也是通用的(pcap规则),过滤器写法可以直接去wireshark验证。

代码使用上,Pcap4J 和 jNetPcap很相似,不同的是,Pcap4J 比jNetPcap 封装的更多些,作者说的是Pcap4J既可以抓包,又可以发包,但是我只用了抓包,更多功能还可以研究一下。

Pcap4J有关的资料,看了看其他的博客都不是很靠谱,也就只有Github上的资料了,所以用是能用上了,但很多问题我也没有找到解决方案。
Pcap4J官网:https://www.pcap4j.org/
Github地址:https://github.com/kaitoy/pcap4j

使用:

第一步:

安装本地抓包库,Linux下安装libpcap;Windows下安装Winpcap或者Npcap

注:2013年3月8日发布以来,WinPcap的开发已经停止,而Npcap仍在开发中。 因此,如果要使用新功能,则应选择Npcap。Linux下运行需要root权限。

第二步:

添加依赖
2019年1月2号更新的版本为1.7.5,maven依赖我们也填这个版本号

<dependencies>
    <dependency>
      <groupId>org.pcap4j</groupId>
      <artifactId>pcap4j-core</artifactId>
      <version>1.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.pcap4j</groupId>
      <artifactId>pcap4j-packetfactory-static</artifactId>
      <version>1.7.5</version>
    </dependency>
       ...
  </dependencies>

抓包:

接下来就可以使用了,我们看一下simple里面Loop.java的代码,去掉了一些无用的sysout:

public class Loop {

  private static final String COUNT_KEY = Loop.class.getName() + ".count";
  private static final int COUNT = Integer.getInteger(COUNT_KEY, 5);
  private static final String READ_TIMEOUT_KEY = Loop.class.getName() + ".readTimeout";
  private static final int READ_TIMEOUT = Integer.getInteger(READ_TIMEOUT_KEY, 10); // [ms]
  private static final String SNAPLEN_KEY = Loop.class.getName() + ".snaplen";
  private static final int SNAPLEN = Integer.getInteger(SNAPLEN_KEY, 65536); // [bytes]

  private Loop() {}

  public static void main(String[] args) throws PcapNativeException, NotOpenException {
String filter = “ip and tcp and (dst host 127.0.0.1 and dst port 80)”; // 设置过滤的字符串

    // 设置要抓包的网卡
    PcapNetworkInterface nif;
    try {
      nif = new NifSelector().selectNetworkInterface(); // 这个方法提供用户输入网卡的序号
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }
    if (nif == null) {
      return;
    }

    // 实例化一个捕获报文的对象,设置抓包参数:长度,混杂模式,超时时间等
    final PcapHandle handle = nif.openLive(SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT);

    // 设置过滤器
    if (filter.length() != 0) {
      handle.setFilter(filter, BpfCompileMode.OPTIMIZE);
    }

    // 观察者模式,抓到报文回调gotPacket方法处理报文内容
    PacketListener listener =
        new PacketListener() {
          @Override
          public void gotPacket(Packet packet) {
            // 抓到报文走这里...
            System.out.println(handle.getTimestamp());
            System.out.println(packet);
          }
        };

    // 直接使用loop无限循环处理包
    try {
      handle.loop(COUNT, listener); // COUNT设置为抓包个数,当为-1时无限抓包
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    handle.close();
  }
}

例子的一些扩展:

过滤器这个字符串可以自己设置,如果拿不准,可以在wireshark上面抓包过滤器上设置一下验证。

设置网卡我们也可以得到所有网卡设备,然后根据IP地址或者网卡名来设置,这样:

// 获取所有网卡设备
List<PcapNetworkInterface> alldev = Pcaps.findAllDevs();
// 根据设备名称初始化抓包接口
PcapNetworkInterface nif = Pcaps.getDevByName(alldev.get(devicenum).getName());

每一个PcapHandle对象对应抓一个网卡的报文,所以要捕获多网卡就要设置多个PcapHandle,我们可以把这个封装成一个方法,传入网卡对象,返回PcapHandle。

public PcapHandle getPcapHandlerB(PcapNetworkInterface nif) throws PcapNativeException {
        PcapHandle.Builder phb = new PcapHandle.Builder(nif.getName()).snaplen(SNAPLEN).promiscuousMode(PromiscuousMode.PROMISCUOUS).timeoutMillis(READ_TIMEOUT);
        return phb.build();
 }

基本的抓包例子就是这样,事实上我们抓到报文一般都会进行一些处理,一些转义。
报文监听的部分我们可以单独用一个类来现实PacketListener,上面的例子只是用于说明。

我们可以设置好PcapHandle对象后,同时实现一个自定义的PacketListener,转异步再调PcapHandle的Loop方法进行抓包和处理,这样就不会阻塞主线程的执行。

除了gotPacket可以捕获,还有一个带返回值的方法:
Packet packet = handle.getNextPacket(); 这个返回抓到的包,是个阻塞方法,就是说可以一个接一个抓到包然后处理。

抓包后处理:

上面的例子讲怎么样用Pcap4J来抓网卡的报文,还是比较简单的,接下来我们说一下,抓到报文之后的处理。

Pcap4J内置支持的协议报文类型

这是Pcap4J内置支持的协议报文类型,库里都有对应的报文实现类
用于描述,我们实现一个PacketListener专门用来处理进来的packet:

public class MyPacketListener implements PacketListener {

    @Override
    public void gotPacket(PcapPacket packet) {
        
        // 开始处理报文
        
        /* 您捕获的数据包包括某些协议的标头和有效负载,如以太网,IPv4和TCP。 
         * Pcap4J的Packet API使您可以从协议标头中获取信息。
         */
        
        IpV4Packet ipV4Packet = packet.get(IpV4Packet.class); // 直接获取IpV4报文
        Inet4Address srcAddr = ipV4Packet.getHeader().getSrcAddr();
        System.out.println(srcAddr); // 输出源IP地址
        
        // 可以直接get你想要的报文类型,只要Pcap4J库原生支持
        EthernetPacket ethernetPacket = packet.get(EthernetPacket.class); // 以太网报文
        TcpPacket tcpPacket = packet.get(TcpPacket.class); // TCP报文
        
        // 也可以通过getPayload()的方式一层一层读取
       EthernetHeader ethernetHeader = ethernetPacket.getHeader(); // 读取以太网帧头部
       IpV4Packet ipV4Packet2 = (IpV4Packet)ethernetPacket4j.getPayload(); // 注意get出来的类型,强转可能抛异常
        
        // 若需要解析的协议Pcap没有支持,那就需要自己实现这个报文的Java类,然后写反序列化方法了
        byte[] rawData = ethernetPacket.getRawData(); // 获取以太网的原始二进制数据
        
        // 然后调你自己对应的反序列化方法解析这个二进制
        // TO-DO ...
        
    }
}

若需要解析的协议Pcap没有支持,可以自己添加,具体可参考,这里不展开了:
https://github.com/kaitoy/pcap4j/blob/v1/www/HowToAddProtocolSupport.md

抓到包后,处理了之后,后面的就是自己的代码逻辑了。

TIPS:

自己使用肯定会遇到各种各样的问题,如果遇到了问题,可以在作者Issues下搜一下有没有类似的:
https://github.com/kaitoy/pcap4j/issues?page=1&q=is%3Aissue+is%3Aclosed

如果没有,那就直接提问吧,作者还是回复比较快的,至少我问的几个问题都回复了。。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值