java本身是不支持处理网络层及以下的协议(用的java8,可能我了解的少,但好像是这个样子)。如果想用java做相关的网络嗅探开发,需要使用一些库,比如jpcap或JNetPcap。推荐JNetPcap,原因是jpcap很早不维护了,功能上也不如jNetPcap。
本文会介绍在windows下配置jNetPcap开发环境,如果是linux会再单写一篇文章说明一些问题。
jNetPcap是对libcap或WinPcap的java封装,通过JNI调用。jNetPcap可以看下这个:https://sourceforge.net/projects/jnetpcap/
对于libpcap/winpcap感觉陌生的话,可以找一些相关资料。
我这里就简单直白的介绍下libpcap/winpcap用途:在计算机,流量都是要流经网络驱动设备的,如果开发人员想要在操作系统抓包,自然是要调相关接口的,然后就有实验室开发了libpcap,提供了相关接口,这样我们开发人员就可以用c语言去调用libpcap的相关接口来进行流量捕获等操作。
那winpcap是什么?libpcap是在linux系统上安装的库,winpcap就是windows上安装的。所以,比如我们在windows上安装一些抓包工具,比如wireshark,可能会需要安装winpcap,当然老的wireshark是用winpcap,我前段时间装了个最新版的,装的是另一个可以替代并支持兼容winpcap的一个库,忘了叫啥了,好像pcap什么吧。如果是像在linux安装tcpdump的话,就是装的libpcap,这都是一个道理。如果了解elasticsearch栈的一个组件packetbeat并用过这个款轻量型网络数据采集器,应该也明白它底层使用的一个流量捕获工具也可以是libpcap,不一定非得是它,也可以使用别的。
当然了,在linux下抓包底层依赖库不一定都是libpcap,之前有在相关论文看到一些更优秀的。
接下来接着说下java jNetPcap怎么调用libpcap/winpcap的。jNetPcap是用java语言封装的一个库,但是它也不是直接就去调用libpcap/winpcap的。而是提供了一个动态连接库:windows下是jnetpcap.dll,linux自然是libjnetpcap.so。jNetPcap通过jni调用jnetpcap.dll的接口,然后jnetpcap.dll的实现再调用winpcap接口,大概就是这么个道理,linux同理。
1. 安装winpcap,原因看上面介绍,怎么安装的,这个话还是上网搜一吧。
2. 配置jnetpcap.dll。这个动态链接库,我给你说下怎么找,首先下载jNetPcap.jar,然后解压开这个jar,在下面这个目录下:
linux和windows下的都有。不要在网上瞎下载,解压开jar包就行,这个jar包我是用maven下的。
把这个jnetpcap.dll放哪呢,放到jvm系统变量 “java.library.path”下,不确定在哪,写个代码打印下:
public static void main(String[] args) {
System.out.println(System.getProperty("java.library.path"));
}
这上面出现的目录下都行,不一定是标红线的,我是放到了jdk的bin目录下(%JAVA_HOME%\bin)。
接下来写代码。
我用的maven,所以先在pom配置这个jar依赖,比较幸运的是这个jar包没有额外依赖,不是maven也很省事。
jnetpcap
jnetpcap
1.4.r1425-1g
这是我在中央仓库找的最新稳定版了(在linux下也没问题),当然了我目前见的最新源码好像都2.0以后了。
代码,打印捕捉到的包,注释我尽量写详细点,这个只是demo:
package com.xuxd.jnetpcap;
import org.jnetpcap.Pcap;
import org.jnetpcap.PcapDLT;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 许晓东 on 2020-05-17.
*/
public class JNetPcapDemo {
public static void main(String[] args) {
start();
}
private static final String DEV_NAME = "\\Device\\NPF_{4F039C40-B2B8-4531-9C75-E6C79FCA3CB9}";
private static void start() {
StringBuilder errbuf = new StringBuilder();
// 获得所有网卡
List ifs = new ArrayList();
if (Pcap.findAllDevs(ifs, errbuf) != 0) {
System.err.println("No interfaces found");
return;
}
Pcap pcap = null;
//查找要监听的网卡进行流量嗅探
for (PcapIf i : ifs) {
//System.out.println(i);
errbuf.setLength(0);
if ((pcap = Pcap.openLive(i.getName(), 64, Pcap.MODE_NON_PROMISCUOUS,
1000, errbuf)) == null) {
System.err.printf("Capture open %s: %s\n", i.getName(), errbuf
.toString());
}
// 混杂模式,看源码注释可能被忽略设置.其实我觉得现在也多用不到,实际中用Java写这个流量嗅探还得是混杂模式的应该不多吧
// 况且说真的,在生活中,交换机现在这么智能,混杂模式不好用呀。
// 本地环回:NULL,
// 通用的话如以太网卡什么的推荐EN10MB,
// 但是可能出现多网卡情况,所以过滤条件写复杂点比如指定ip呀mac地址什么
// 这只是个demo写的简单了
// 所以实际开发此处需要注意考虑监听网上配置,或者尝试启用混杂模式
// 我这与这个代码的时候用的无限网卡,我看下值是30,这里就写这个30写死了,仅供参考
if (pcap.datalink() == PcapDLT.EN10MB.getValue() && i.getFlags() == 30) {
System.out.printf("Opened interface\n\t%s\n\t%s\n", i.getName(), i
.getDescription());
System.out.printf("Warnings='%s'\n", errbuf.toString());
break;
} else {
pcap.close();
pcap = null;
}
}
if (pcap == null) {
System.err.println("Unable to find interface");
return;
}
pcap.loop(Pcap.LOOP_INFINATE, (PcapPacket packet, String userObject) -> {
System.out.println(userObject);
// 打印报文
System.out.println(packet);
}, "xuxd");
System.out.println("start capture...");
}
}
然后,刷新了下csdn,下面截个图看下,打印的报文信息:
从传输层以下都有。
这个可能用处不大,有时候需要分析报文信息,我捕获一个Http报文为例:
改下代码,对报文简单作个处理,获取 http请求的报文并打印:
pcap.loop(Pcap.LOOP_INFINATE, (PcapPacket packet, String userObject) -> {
//System.out.println(userObject);
// 打印报文
//System.out.println(packet);
// 打印http报文
if (packet.hasHeader(Http.ID)) {
Http http = new Http();
packet.getHeader(http);
// 打印请求头
System.out.println(http);
// 打印请求体
System.out.println(new String(http.getPayload()));
}
}, "xuxd");
打印报文如下, 一个http响应报文:
当然了,我写的这个只是demo,看起来比较Low,仅供参考