程序开发的基础知识

了解 ARP 协议如何工作,在什么时候要使用

清楚 ARP 帧的构成

懂得构建一个 ARP 请求数据帧

懂得接受和处理活动主机返回的响应 ARP 帧

懂得使用所编写程序打开电脑里面的对应网卡,并把它的混杂模式打开

因为平台为 Java,要学会使用 jpcap 包来调用 winpcap 对应函数

获得 IP 地址与 MAC 地址的对应关系并显示

熟练运用 Java,灵活运用线程

设计思路

首先寻找并打开适合的网络适配器,然后根据输入的局域网 IP 地址构造 ARP 的数据包,然后通过 Java 对网卡数据捕获需要用到 jpcap,jpcap 调用 wincap 实现数据包的发送,并监听返回的数据包,分析局域网内返回的活动主机的 IP 地址和物理地址并打印。

程序流程图

java发送arp包代码 java arp协议_System

关键数据结构

java发送arp包代码 java arp协议_java发送arp包代码_02

关键性的代码

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import java.util.Arrays;
import java.net.InetAddress;
import java.net.Inet4Address;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
public class ARPSearch {
    public static ARPPacket getTargetMAC(InetAddress targetIp) {
        NetworkInterface[] 	devices = JpcapCaptor.getDeviceList();
        NetworkInterface 	device 	= null;
// 寻找适合的网络设备
loop:
        for (NetworkInterface d : devices) {
            for (NetworkInterfaceAddress addr : d.addresses) {
                if (!(addr.address instanceof Inet4Address))	continue;
                byte[] bip 		= targetIp.getAddress();
                byte[] subnet 	= addr.subnet.getAddress();
                byte[] bif 		= addr.address.getAddress();
                for (int i = 0; i < 4; i++) {
                    bip[i] 		= (byte) (bip[i] & subnet[i]);
                    bif[i] 		= (byte) (bif[i] & subnet[i]);
                }
                if (Arrays.equals(bip, bif)) {
                    device = d;
                    break loop;
                }
            }
        }
        if (device == null)	{
            ARPFace.textArea.append(targetIp+ " is not a local address");
            throw new IllegalArgumentException(targetIp+ " is not a local address");
        }
        JpcapCaptor captor = null;
// 打开一个网络数据包捕捉者
        try {
            captor = JpcapCaptor.openDevice(device, 2000, false, 300);
// 只接收ARP数包
            captor.setFilter("arp", true);
        } catch (Exception e) {	}
// 获得发送数据包的实例
        JpcapSender sender 	= captor.getJpcapSenderInstance();
        InetAddress srcip 	= null;
        for (NetworkInterfaceAddress addr : device.addresses)
            if (addr.address instanceof Inet4Address) {
                srcip 		= addr.address;
                break;
            }
// 进行广播数据报的MAC地址
        byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255,(byte) 255, (byte) 255, (byte) 255 };
// 构造REQUEST 类型的ARP的数据包
        ARPPacket arp 			= new ARPPacket();
        arp.hardtype 			= ARPPacket.HARDTYPE_ETHER;
        arp.prototype 			= ARPPacket.PROTOTYPE_IP;
        arp.operation 			= ARPPacket.ARP_REQUEST;
        arp.hlen 				= 6;
        arp.plen 				= 4;
// 源MAC地址
        arp.sender_hardaddr 	= device.mac_address;
// 源IP地址
        arp.sender_protoaddr 	= srcip.getAddress();
// 目地MAC地址:广播地址全为1(二进制)
        arp.target_hardaddr 	= broadcast;
// 目地IP地址
        arp.target_protoaddr 	= targetIp.getAddress();
// 构造以太网头部
        EthernetPacket ether 	= new EthernetPacket();
        ether.frametype 		= EthernetPacket.ETHERTYPE_ARP;
        ether.src_mac 			= device.mac_address;
        ether.dst_mac 			= broadcast;
// ARP数据包加上以网关头部
        arp.datalink 			= ether;
// 向局域网广播ARP请求数据报
        sender.sendPacket(arp);
// 接收目标主面的答应ARP数据报
        while (true) {
            ARPPacket p = (ARPPacket) captor.getPacket();// 接收返回包
            if (p == null) {
                System.out.println(targetIp + "不是本地局域网的IP号");
                ARPFace.textArea.append(targetIp + "不是本地局域网的IP号\n");
                return p;
            } else if(Arrays.equals(p.target_protoaddr, srcip.getAddress())) {
                return p;
            }
        }
    }
    public static void main(String[] args) throws Exception {
        String s = "192.168.43.";
        for (int n = 1; n <= 254; n++) {
            System.out.println(s+n);
            ARPPacket arpP = ARPSearch.getTargetMAC(InetAddress.getByName(s+n));
            if(arpP==null) {		}
            else {
                System.out.println("硬件类型:" 		+ arpP.hardtype);
                System.out.println("操作类型:" 		+ arpP.operation);
                System.out.println("源 MAC 地址:" 	+ arpP.getSenderHardwareAddress());
                System.out.println("源 IP 地址 :" 	+ arpP.getSenderProtocolAddress());
                System.out.println("目标 MAC 地址    " 	+ arpP.getTargetHardwareAddress());
                System.out.println("目标 IP 地址     " 	+ arpP.getTargetProtocolAddress());
                System.out.println("===================================");
            }
        }
    }
}
import java.net.InetAddress;
import java.net.UnknownHostException;
import jpcap.packet.ARPPacket;
public class ARPThread extends Thread {
    ARPThread() {}
    public void run () {
        while(ARPFace.n<=254 && ARPFace.flag) {
            System.out.println(ARPFace.netIP+ARPFace.n);
            ARPFace.textArea.append(ARPFace.netIP+ARPFace.n+"\n");
            ARPPacket arpP;
            try {
                arpP = ARPSearch.getTargetMAC(InetAddress.getByName(ARPFace.netIP+ARPFace.n));
                if(arpP==null) {			}
                else {
                    ARPFace.textArea.append("硬件类型:" 	+ arpP.hardtype+"\n");
                    ARPFace.textArea.append("操作类型:" 	+ arpP.operation+"\n");
                    ARPFace.textArea.append("源 MAC 地址:"	+ arpP.getSenderHardwareAddress()+"\n");
                    ARPFace.textArea.append("源 IP 地址 :" 	+ arpP.getSenderProtocolAddress()+"\n");
                    ARPFace.textArea.append("目标 MAC 地址    "	+ arpP.getTargetHardwareAddress()+"\n");
                    ARPFace.textArea.append("目标 IP 地址     " + arpP.getTargetProtocolAddress()+"\n");
                    ARPFace.textArea.append("===================================\n");
                    System.out.println("硬件类型:" 		+ arpP.hardtype);
                    System.out.println("操作类型:" 		+ arpP.operation);
                    System.out.println("源 MAC 地址:" 	+ arpP.getSenderHardwareAddress());
                    System.out.println("源 IP 地址 :" 	+ arpP.getSenderProtocolAddress());
                    System.out.println("目标 MAC 地址    " 	+ arpP.getTargetHardwareAddress());
                    System.out.println("目标 IP 地址     " 	+ arpP.getTargetProtocolAddress());
                    System.out.println("===================================");
                }
                ARPFace.n++;
                if (ARPFace.n > 254)	ARPFace.n =1;
            } catch (UnknownHostException e) {
// TODO 自动生成的 catch 块
                printStackTrace();
            }
        }
    }
}
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
public class ARPFace extends JFrame {
    private static final long serialVersionUID = 12L;
    private	JLabel 				lab 			= new JLabel("输入局域网号:");
    private	JTextField 			text 			= new JTextField(15);
    static  JTextArea 			textArea 		= new JTextArea();
    private JPanel    			topBar			= new JPanel();
    private JButton				start 			= new JButton("开始");
    private JButton				stop 			= new JButton("停止");
    private JButton				clear 			= new JButton("重置");
    private JButton				close			= new JButton("关闭");
    private Font 				buttonFont 		= new Font("宋体",Font.BOLD,30);
    Toolkit 					kit 			= Toolkit.getDefaultToolkit();
    Dimension 					screenSize		= kit.getScreenSize();
    static 	boolean 			flag			= true;					//重要判断标志——用于结束抓包线程——静态
    static  int					n				= 1;
    static 	String				netIP			= null;
    String 	pattern								= "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\..*$";//正则表达式
    ARPFace() {
        Face();
        Listeners();
    }
    public void Face() {
        setTitle("ARP协议获取局域网内部活动主机的物理地址");
        setSize(960,800);
        setLocation(2*screenSize.width/4,screenSize.height/8);
        add(topBar,BorderLayout.NORTH);
        topBar.add(lab);
        topBar.add(text);
        topBar.add(start);
        topBar.add(stop);
        topBar.add(clear);
        topBar.add(close);
        lab.setFont(buttonFont);
        text.setFont(buttonFont);
        start.setFont(buttonFont);
        stop.setFont(buttonFont);
        clear.setFont(buttonFont);
        close.setFont(buttonFont);
        textArea.setFont(new Font("宋体", Font.PLAIN, 20));
        textArea.setMargin(new Insets(3,10,3,10));
        textArea.setLineWrap(true);
        textArea.setDragEnabled(true);
        JScrollPane panel = new JScrollPane(textArea,
                                            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                                            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        getContentPane().add(panel, BorderLayout.CENTER);
    }
    public void Listeners() {
        start.addActionListener(this::Start);
        stop.addActionListener(this::Stop);
        clear.addActionListener(this::Clear);
        close.addActionListener(this::Close);
    }
    public void Start(ActionEvent event) {
        ARPFace.netIP = text.getText();
        if (ARPFace.netIP != null && ARPFace.netIP.matches(pattern)) {
            flag = true;
            ARPThread arpt = new ARPThread();
            arpt.start();
        } else {
            textArea.append("请您输入你需要查看的网络。\n");
            JOptionPane.showOptionDialog(null, "请输入正确的网络号","提示",JOptionPane.DEFAULT_OPTION,JOptionPane.INFORMATION_MESSAGE,null, null, null);
        }
    }
    public void Stop(ActionEvent event) {
        flag = false;
        try {
            Thread.sleep(2000);
            textArea.append("|**************已停止**************|\n");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void Clear(ActionEvent event) {
        flag 			= false;
        ARPFace.n 		= 1;
        text.setText(null);
        ARPFace.netIP 	= null;
        try {
            Thread.sleep(2000);
            textArea.setText("");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void Close(ActionEvent event) {
        System.exit(0);
    }
    public static void main(String[] args) {
// TODO 自动生成的方法存根
        ARPFace arf = new ARPFace();
        arf.setVisible(true);
    }
}

测试结果与程序截图

主界面

java发送arp包代码 java arp协议_IP_03

输入局域网号

java发送arp包代码 java arp协议_IP_04

开始运行

java发送arp包代码 java arp协议_java_05

输入的局域网号不正确

java发送arp包代码 java arp协议_IP_06

开发过程中遇到的问题及解决办法

不知道如何在程序中打开合适的网络适配器并把它设置为混杂模式

通过上完查找代码,学会了如何打开对应的网卡和把它设置为混杂模式。

不知道如何构建 ARP 请求包

先通过看课本彻底地了解了 ARP 协议运行的过程,再通过网上查找 ARP 数据包的构成,在掌握透彻之后,修改成我需要的,然后在代码中加以运用。

java发送arp包代码 java arp协议_java_07

不熟悉用 jpcap 调用 winpcap 的相关函数

通过在网上查阅相关资料,最终把这次编程需要使用到的一些基本知识掌握后就暂时没有继续学习下去。

程序中待解决的问题及改进方向

程序的整体运行还是没有太大的问题,但输入局域网号的正误的正则表达式还没有完善,需要做更多的判定,并且由于对 Java 的线程相关的知识还不是理解得很透彻,总体来说程序运行的响应速度比较慢,等待的时间过长,需要在后续时间进行优化。