计算机网络实验-网络嗅探器
解释:
这次实验的题目使用Java来实现的,主要就是需要用到Java的很多的包和类来实现网络的一些功能,比如抓包,绑定网卡…等操作。
当然我使用的是IntelliJ IDEA Community Edition 2018.3.2 x64(简称IDE),这个过程也需要一些教程,我会在另外的一个博客里面细致讲述吧。
地址: 安装地址
总体的实验也是根据一些大佬的指导,参考。比如 >大佬一<, ,>大佬二<。
先看一下这个简单的代码(此代码同样是来自上面那位大佬的)
代码1:
import java.io.IOException;
import jpcap.*;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
import jpcap.packet.TCPPacket;
public class Main {
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;
try {
jpcap = JpcapCaptor.openDevice(devices[0], caplen, promiscCheck, 50);
//0 或 1
} 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:
TCPPacket tcp = (TCPPacket) packet;// 强转
protocol = "TCP";
break;
case 17:
protocol = "UDP";
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("----------------------------------------------");
}
}
}
}
改的过程主要就是根据老师给的题目和查阅jpcap的JDK而得到的。
我把一些JDK整理了一下:JDK查阅地址
改变代码2:
import jpcap.*;
import jpcap.packet.Packet;
import jpcap.packet.IPPacket;
import jpcap.packet.TCPPacket;
import jpcap.packet.ICMPPacket;
import jpcap.packet.UDPPacket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.io.IOException;
public class Sniffer {
public static void main(String[] args) throws IOException {
/************* 第一步:绑定网卡****************/
NetworkInterface[] devices = JpcapCaptor.getDeviceList();//获取本机的网络接口列表
// NetworkInterface[]搜索具有绑定到其的指定Internet协议(IP)地址的网络接口的便捷方法。
//如果指定的IP地址绑定到多个网络接口,则不会定义返回哪个网络接口。
// 此类表示由名称和分配给此接口的IP地址列表组成的网络接口。
// 它用于标识加入多播组的本地接口。接口通常以诸如“le0”之类的名称而为人所知。
//接口对象包含了对应网络接口的一些信息,名称、描述、IP以及MAC地址以及数据链路层名称和描述
//输出各种网卡
for(int i = 0 ; i < devices.length ; i++){
System.out.println(devices[i].name +" 网卡描述: "+devices[i].description);
}
System.out.println("-------------------------------------------");
// 一旦有了网络接口列表就可以从选定用于捕获数据包的网络接口,
// 可以使用方法JpcapCaptor.openDevice()来打开网络接口
System.out.println("请输入你想要测试的网卡: (此电脑在链接YTU时只有‘1’可以用,在连接有线时‘0’也可用)");
Scanner reader = new Scanner(System.in);
int n = reader.nextInt();
JpcapCaptor jpcap = null;
try {
jpcap = JpcapCaptor.openDevice(devices[n], 65535, true, 10);
//0 或 1
// 初始化网络接口,并且返回Jpcap类的实例:
// static Jpcap openDevice(String device,int snaplen,boolean promisc ,int to_ms)
// device参数是网络接口的名字;
// snaplen参数是一次捕获的最大字节数量;
// promisc参数,如果为true,接口被设置为混杂模式;
// to_ms参数,设定processPacket()中的Timeout;当指定接口不能被打开抛出IO异常。
} catch (IOException e) {
e.printStackTrace();
//在命令行打印异常信息在程序中出错的位置及原因。
}
//catch(…)能够捕获多种数据类型的异常对象,所以它提供给程序员一种对异常对象更好的控制手段,
// 使开发的软件系统有很好的可靠性。
// jpcap.setFilter("IP and TCP",true);
/*二:抓包*/
// 过滤
System.out.println("请输入你想要找的协议:");
System.out.println("****************************");
System.out.println("提示: -1 表示输出全部的协议 ");
System.out.println(" 1 表示ICMP协议 ");
System.out.println(" 6 表示TCP协议");
System.out.println(" 17 表示UDP协议");
System.out.println("****************************");
int s0 = reader.nextInt();
String protocols = "";
int k=0;
int counts=0;
while( k < 10 ){
Packet packet = jpcap.getPacket();
//返回一个捕获的数据包
if (packet instanceof IPPacket ){
//判断其左边对象是否为其右边类的实例,返回boolean类型的数据
IPPacket ip = (IPPacket) packet;// 强转
// 进行过滤初始化处理
int s1;
s1=new Integer( ip.protocol ) ;
if(s0==-1)
s0=s1;
k++;
// 进入协议输出处理
if(s0==s1) {
counts++;
// 将字节码转换为ascii
byte [] b;
b=ip.data;
String s=new String(b, StandardCharsets.US_ASCII);
// 输出显示
System.out.println(" 这是抓到的第" + k + "个包");
System.out.println("版本:IPv" + ip.version);
System.out.println("数据包长度:" + ip.length);
System.out.println("数据内容:" + s);
System.out.println("标识:" + ip.ident);
if (ip.version == 4) {
System.out.println("IPv4 报头的选项:" + ip.option);
System.out.println("片偏移:" + ip.offset);
System.out.println("DF:Don't Fragment: " + ip.dont_frag);
System.out.println("MF:More Fragment: " + ip.more_frag);
System.out.println("分组标识:" + ip.ident);
}
else {
System.out.println("IPv6 报头的选项:" + ip.options);
}
switch (s0) {
case 1:
protocols = "ICMP";
ICMPPacket icmp = (ICMPPacket) packet;// 强转
System.out.println("ICMP部分");
System.out.println("ICMP报文类型:" + icmp.type);
System.out.println("ICMP报文代码: " + icmp.code);
System.out.println("校验和:" + icmp.checksum);
System.out.println("序列号:" + icmp.seq);
System.out.println("子网掩码:" + icmp.subnetmask);
System.out.println("生存时间:" + icmp.alive_time);
System.out.println("发起时间戳:" + icmp.orig_timestamp);
System.out.println("传送时间戳:" + icmp.trans_timestamp);
break;
case 6:
protocols = "TCP";
TCPPacket tcp = (TCPPacket) packet;// 强转
System.out.println("TCP部分");
System.out.println("源端口号: " + tcp.src_port);
System.out.println("目的端口号: " + tcp.dst_port);
System.out.println("窗口: " + tcp.window);
System.out.println("序号: " + tcp.sequence);
System.out.println("URG标志: " + tcp.urg);
System.out.println("紧急指针: " + tcp.urgent_pointer);
System.out.println("ACK标志: " + tcp.ack);
System.out.println("ACK确认号: " + tcp.ack_num);
System.out.println("FIN标志: " + tcp.fin);
break;
case 17:
protocols = "UDP";
UDPPacket udp = (UDPPacket) packet;// 强转
System.out.println("UDP部分");
System.out.println("源端口号" + udp.src_port);
System.out.println("目的端口号" + udp.dst_port);
System.out.println("UDP包长度 " + udp.length);
break;
default:
break;
}
System.out.println("----------------------");
System.out.println("协议类型:" + protocols);
System.out.println("未知:" + ip.d_flag);
System.out.println("生存期:" + ip.hop_limit);
System.out.println("数据包长度:" + ip.length);
System.out.println("优先权:" + ip.priority);
System.out.println("源IP " + ip.src_ip.getHostAddress());
System.out.println("目的IP " + ip.dst_ip.getHostAddress());
System.out.println("服务类型: " + ip.rsv_tos);
System.out.println("区分服务:最大的吞吐量: " + ip.t_flag);
System.out.println("区分服务:最高的可靠性:" + ip.r_flag);
System.out.println("源主机名: " + ip.src_ip);
System.out.println("目的主机名: " + ip.dst_ip);
System.out.println("----------------------------------------------");
}
}
}
if ( counts == 0 )
System.out.println("在此次抓包中没有你想要的协议!");
}
}
注:
一: 一些简单API的查阅
1. 获取网络接口列表
要想从网络中捕获数据包,第一件必须要做的事就是获取本机的网络接口列表。Jpcap提供了方法JpcapCaptor.getDeviceList()完成这个任务,该方法返回一组NetworkInterface对象。 NetworkInterface接口对象包含了对应网络接口的一些信息,例如:名称、描述、IP以及MAC地址以及数据链路层名称和描述。
2. 打开网络接口
一旦有了网络接口列表就可以从选定用于捕获数据包的网络接口,可以使用方法,JpcapCaptor.openDevice()来打开网络接口。
3. 从网络接口捕获数据包
一旦获得了JpcapCaptor实例就可以用来捕获来自网络接口的数据包。使用JpcapCaptor实例来捕获数据包主要有两种方法: 回调(callback)以及逐个捕获(one-by-one)
回调方法
实现的细节: 首先定义一个实现PacketReceiver接口的类。PacketReceiver接口中定义了receivePacket()方法,只需实现receivePacket()这个方法。
然后可以使用回调的方法调用JpcapCaptor.processPacket()或JpcapCaptor.loopPacket()方法开始数据包的捕获。processPacket()或loopPacket()方法可以指定捕获的数据包的数量。-1表示无限地捕获数据包。
两种回调方法:processPacket()和loopPacket()非常相似。通常建议使用processPacket(),因为它支持超时以及非阻塞模式,而loopPacket()并不支持。
逐个捕获:
使用回调方法稍微有点复杂,因为并不知道Jpcap什么时候调用回调方法。如果不使用回调方法,可以调用JpcapCaptor.getPacket()方法来捕获数据包。
getPacket()只是简单返回一个捕获的数据包,可以多次使用getPacket()方法捕获连续的数据包。
4. 设置捕获过滤器
在Jpcap中可以设置过滤器使得Jpcap不捕获不需要的数据包。例如:如果仅仅只需捕获TCP/IPv4数据包,就可以设置过滤器,其方法如下例所示:
5. 将捕获的数据包存挡
可以将捕获的数据包写入一个二进制文件,事后使用Jpcap或支持tcpdump格式文件的其它应用程序进行查询。
存储捕获的数据包首先需要使用JpcapWriter.openDumpFile()打开一个文件,参数分别是用来捕获数据包的一个JpcapCaptor实例以及String文件名。
一旦通过openDumpFile()方法获得一个JpcapWriter的实例,就可以使用JpcapWriter.writePacket()存储捕获的数据包。将所有要存储的数据都存储之后,必须使用JpcapWriter.close()方法关闭打开的文件。
6. 读入文件中的数据包
在Jpcap中,可以使用JpcapCaptor.openFile()方法打开一个由JpcapWriter存储的文件,并从中读入数据包。类似于JpcapCaptor.openDevice()方法,JpcapCaptor.openFile()方法将返回一个JpcapCaptor类的实例,因此可以使用“从网络接口捕获数据包”中描述的方式来从文件中读取数据包。
7. 通过网络接口发送数据包
发送一个数据包首先需要调用JacapSender.openDevice()或JacapSender.getJpcapSenderInstance()方法。
一旦获得一个JpcapSender实例,就可以将Packet类实例传递给JpcapSender.sendPacket()方法。
二: 关于Java写入文件的一些方法:
三: 用java关于将 byte 数组里存的是ascii码,怎么转成字符串
byte[] b=new byte[]{65,66,67,68};//字节数组
String s=new String(b,“ascii”);//第二个参数指定编码方式
System.out.print(s);
四: Java中比较两个字符串是否相等的问题
下面将分析使用 (注意:Java中 = 是赋值运算符, 是比较是否相等) 和 equals()方法 来比较两个字符串相等的区别:
“==” 比较的是两个字符串的地址是否为相等(同一个地址), equals() 方法比较的是两个字符串对象的内容是否相同(当然,若两个字符串引用同一个地址,使用equals()比较也返回true)。
例1. 使用new关键字声明两个String类型的变量
String s1 = new String("abc");
String s2 = new String("abc");
//分别使用.equals()和==进行比较
if(s1.equals(s2))
{
System.out.println("可以使用 equals 来比较");
}else
{
System.out.println("不可以使用 equals 来比较");
}
if(s1 == s2)
{
System.out.println("可以使用== 来比较");
}else
{
System.out.println("不可以使用== 来比较");
}
运行程序发现,用equals比较返回true,用 == 比较返回false。原因如下:
因为 “” 比较的是两个字符串对象的地址是否相同(是否为同一个地址),当使用new关键字创建一个对象的时候,该对象单独占据一块存储空间,存放llg这个字符串的值。所以s1 s2两个字符串虽然值相同,但是存储的地址不是一个地址,例如两个人都叫l“李四”但是他们的住址不在一个地方。当使用 “”来比较的时候,比较的是两个字符串的地址是否是同一个,所以返回false。但是使用equals()方法比较这两个字符串,将会比较两个字符串的值是否相同,所以返回true。
五: Java写入文件实现换行
file.write( " \r\n " );
最后放一个自已写的东西吧。
其实这个写的就像是数据结构,不断地重复工作,实现一些功能。
import jpcap.*;
import jpcap.packet.Packet;
import jpcap.packet.IPPacket;
import jpcap.packet.TCPPacket;
import jpcap.packet.ICMPPacket;
import jpcap.packet.UDPPacket;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.io.IOException;
import java.util.regex.*;
public class sniffers {
public static void main(String[] args) throws IOException {
/************* 第一步:绑定网卡****************/
NetworkInterface[] devices = JpcapCaptor.getDeviceList();//获取本机的网络接口列表
for (int i = 0; i < devices.length; i++) {
System.out.println(devices[i].name + " 网卡描述: " + devices[i].description);
}
System.out.println("-------------------------------------------");
System.out.println("请输入你想要测试的网卡: ");
Scanner reader = new Scanner(System.in);
int n = reader.nextInt();
// 初始化网络接口
JpcapCaptor jpcap = null;
try {
// 初始化网络接口,并且返回Jpcap类的实例:
jpcap = JpcapCaptor.openDevice(devices[n], 65535, true, 10);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("请输入你想要找的指定的目的IP地址:");
System.out.println("提示: 1.输入你想要找的源IP地址(形如 202.194.116.116)");
System.out.println(" 2.“-1”:任意源IP地址");
String IPSourcein=reader.next();//指定源IP
System.out.println("请输入你想要匹配的字符串:");
System.out.println("提示: 任何有关字符皆可(空串也符合)");
String str=reader.next(); //字符串匹配
System.out.println("请输入你想要找的协议:");
System.out.println("****************************");
System.out.println("提示: -1 表示输出全部的协议 ");
System.out.println(" 1 表示ICMP协议 ");
System.out.println(" 6 表示TCP协议");
System.out.println(" 17 表示UDP协议");
System.out.println("****************************");
int s0 = reader.nextInt();//选择协议
// 变量参数声明
String protocols = null;
int IPpacketnum = 0; //抓到IP包的个数
int IPnum=0; //IP个数
int Pnum = 0; //协议个数
int Stringnum=0; //字符个数
while (IPpacketnum < 50) {
Packet packet = jpcap.getPacket();
if (packet instanceof IPPacket) {
IPpacketnum++;
IPPacket ip = (IPPacket) packet;// 强转
// 将字节码转为ascii
byte[] b;
b = ip.data;
String s = new String(b, StandardCharsets.US_ASCII);
// 源IP处理
String IPSourceexam="-1";
String IPSourceget=ip.src_ip.getHostAddress();
if(IPSourcein.equals(IPSourceexam)){
IPSourcein=IPSourceget;
}
// 子串匹配处理
int index=s.indexOf(str);
// 协议号处理
int s1; //协议号
s1 = new Integer(ip.protocol);
if (s0 == -1)
s0 = s1;
if( IPSourcein.equals(IPSourceget) && index != -1 ) {
Stringnum++;
IPnum++;
if (s0 == s1) {
Pnum++;
// 输出显示
System.out.println(" 这是抓到的第" + IPpacketnum + "个IP包");
System.out.println("版本:IPv" + ip.version);
System.out.println("数据包长度:" + ip.length);
System.out.println("数据内容:" + s);
System.out.println("标识:" + ip.ident);
if (ip.version == 4) {
System.out.println("IPv4 报头的选项:" + ip.option);
System.out.println("片偏移:" + ip.offset);
System.out.println("DF:Don't Fragment: " + ip.dont_frag);
System.out.println("MF:More Fragment: " + ip.more_frag);
System.out.println("分组标识:" + ip.ident);
} else {
System.out.println("IPv6 报头的选项:" + ip.options);
}
switch (s0) {
case 1:
protocols = "ICMP";
ICMPPacket icmp = (ICMPPacket) packet;// 强转
System.out.println("ICMP部分");
System.out.println("ICMP报文类型:" + icmp.type);
System.out.println("ICMP报文代码: " + icmp.code);
System.out.println("校验和:" + icmp.checksum);
System.out.println("序列号:" + icmp.seq);
System.out.println("子网掩码:" + icmp.subnetmask);
System.out.println("生存时间:" + icmp.alive_time);
System.out.println("发起时间戳:" + icmp.orig_timestamp);
System.out.println("传送时间戳:" + icmp.trans_timestamp);
break;
case 6:
protocols = "TCP";
TCPPacket tcp = (TCPPacket) packet;// 强转
System.out.println("TCP部分");
System.out.println("源端口号: " + tcp.src_port);
System.out.println("目的端口号: " + tcp.dst_port);
System.out.println("窗口: " + tcp.window);
System.out.println("序号: " + tcp.sequence);
System.out.println("URG标志: " + tcp.urg);
System.out.println("紧急指针: " + tcp.urgent_pointer);
System.out.println("ACK标志: " + tcp.ack);
System.out.println("ACK确认号: " + tcp.ack_num);
System.out.println("FIN标志: " + tcp.fin);
break;
case 17:
protocols = "UDP";
UDPPacket udp = (UDPPacket) packet;// 强转
System.out.println("UDP部分");
System.out.println("源端口号" + udp.src_port);
System.out.println("目的端口号" + udp.dst_port);
System.out.println("UDP包长度 " + udp.length);
break;
default:
break;
}
System.out.println("----------------------");
System.out.println("协议类型:" + protocols);
System.out.println("未知:" + ip.d_flag);
System.out.println("生存期:" + ip.hop_limit);
System.out.println("数据包长度:" + ip.length);
System.out.println("优先权:" + ip.priority);
System.out.println("源IP " + ip.src_ip.getHostAddress());
System.out.println("目的IP " + ip.dst_ip.getHostAddress());
System.out.println("服务类型: " + ip.rsv_tos);
System.out.println("区分服务:最大的吞吐量: " + ip.t_flag);
System.out.println("区分服务:最高的可靠性:" + ip.r_flag);
System.out.println("源主机名: " + ip.src_ip);
System.out.println("目的主机名: " + ip.dst_ip);
System.out.println("----------------------------------------------");
// 写入文件
try {
FileWriter file = new FileWriter("1.txt", true);
file.write(IPpacketnum+". "+s+"\r\n");
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
if (IPpacketnum==0)
System.out.println("此网卡没有抓到IP!");
else if(IPnum == 0)
System.out.println("没有找到指定的源IP地址!");
else if(Stringnum==0)
System.out.println("没有找到能够匹配的串!");
else if (Pnum == 0)
System.out.println("在此次抓包中没有你想要的协议!");
else{
System.out.println("IP数据报数据内容已写入文件中,查看后是否需要清除该文件夹?");
System.out.println("提示: 输入 ”Yes“:删除!");
System.out.println(" 输入 “任意键”: 不删除!");
String s2 = reader.next();
String s3 = "Yes";
if(s2.equals(s3)){
try{
File file = new File("1.txt");
if(file.delete()){
System.out.println(file.getName() + " 文件已被删除!");
}else{
System.out.println("文件删除失败!");
}
}catch(Exception e){
e.printStackTrace();
}
}
else
System.out.println("未删除此文件夹!");
}
}
}