飞天老鼠

ARP防火墙

     像偶等长期在外流浪一簇,靠租个小房间的过日子,与人共享一条宽带上网。本来嘛,两三兆的带宽供四五个人上网肯定是没啥问题。可是这年头,一说下载就是P2P,像Bit Torrent(这东西叫“变态”,还真是传神,就因为太变态,有些国家的ISP都封P2P了),迅雷都是比较出了名的耗带宽的。迅雷虽然可以限速,可是会有多少人会去设呢?而且还带了bt功能。bt那东西嘛,设了限速基本没啥用,特别是对于ADSL的网络,有人下bt,别人基本就别想好好上网了,比起56k的猫也要慢上不少。于是呼,像P2P终结者等软件就开始大行其道(当然也有不少人也是为了独占带宽用的)。最近,这个网络里面除了个别人,其他就都别想上网了,严重时大家都没法上网了。这个时候,大家可能会想到用ARP防火墙来防止ARP攻击,这个时候运气好的话,网速变快了,如果ARP防火墙功能比较弱的话,就变得没无效或者网速变得比原来还慢,甚至彻底不能上网了。这个时候,我的土ARP防火墙就该出马了,再加上一些限制别人网速的功能,就能解决别人下P2P时候网速太慢的问题了。为什么说土呢,就一Console的程序,总长度还没超过100行。

    与ARP相关的软件很多,有像p2p终结者那样的流量控制的,还有像Cain那样的嗅探用的,还有像各种ARP病毒等。这些都利用了一个原理就是ARP欺骗,利用ARP欺骗来实现会话劫持。下面介绍一下ARP欺骗的原理。

    在以太网内,机器间的交互是通过mac地址来进行的,通常机器间的交互只要知道对方机器的IP地址即可,那么,通讯的双方是如何把IP地址转化成mac地址的呢?正常情况下,如果机器A需要与机器B进行通讯,在机器A不知道机器B的mac地址(MACB)的情况下,机器A会向网络广播一个ARP请求数据包(会带上机器A的IP地址IPA与mac地址MACA),询问谁是IPB。这个时候IPB接收到这个数据包后会向机器A发一个ARP响应数据包告诉机器A,IPB对应的mac地址是MACB。这样机器A与机器B都知道了对方的MAC地址了,就能互相通讯了。用简单的语言来描述的话,就是:A喊一声,IPB的MAC地址是什么,然后B告诉A,B的MAC地址是MACB。

    那么ARP欺骗是怎么回事呢?这个时候机器C来了,C一直声称IPB对应的MAC地址是MACC,因为A不知道谁说的是正确的,它会以第一个接收到的数据包为准,所以,只要C一直在说,把B的回应淹没掉就可以了。这样A一直会认为IPB的MAC地址就是MACC了。这个时候C就可以伪装成B与A通讯了。这只是做到了ARP欺骗的第一步,并没有太大的用处。那么接下去继续利用ARP欺骗来做到会话劫持。正常情况下,A与B之前会直接进行通讯,这个时候,C又来捣乱了,C用上面的方式欺骗了A,让A认为C就是B,同时C也用上面的方式欺骗了B,让B认为了C就是A。此时A本来想发给B的数据包以及B本来想发给C的数据包都发给了C,C只要做一下转发,转发给本来就应该接收的那方。从A与B的角度来说,两者都是正常通讯的,只是大家都不知道,中间还有个C。此时会话劫持已经完成,C可以做很多事情了,如:控制流量,禁止某些数据包通过,篡改某些数据包,获取数据包里的信息等等。

     那么如何防范会话劫持呢。根据上述原理,只要有一方不被欺骗,劫持就不成立了。假如我用的机器就是A机器,那么只对A有控制权,自然只能在A上做手脚了。我们知道操作系统都维护有一个ARP Cache,Cache里存的就是IP与MAC的映射。要防止有人仿冒B,只要Cache里关于B的映射是正确的就行了。操作系统给我们提供了一种方法,那就是可以设置静态的映射,除非手工重新设置,那么在重启系统之前是不会变的,那么我们只要拿到正确的B的MAC地址就可以。好在B在绝大部分情况下就是网关,所以很容易拿到正确的MAC地址。剩下的就很简单了。

    在Windows下可以用如下的命令,前面是IP地址,后面是MAC地址linux下也用类似的命令,形式稍有不同,具体可以参见linux下arp命令的帮助。

arp -s 192.168.1.1 00-aa-00-62-c6-09

    这样再也不可能有机器来来仿冒成B了。很多ARP防火墙其实只实现到了这一层。这个时候,很多同学都会发现:怎么用了ARP防火墙之后,网速反而慢了啊!这个时候其实还没完,因为C还可以欺骗B啊。但是C对B的欺骗也不完全,有时候B能认对A有时候不能,所以B发出的数据,有时候会发给A,有时候发给C,而通过C再转发给A的数据,对于类似于TCP这样的协议,由于C没有与A建立会话,所以,绝大部分的数据包都会丢弃,TCP的连接也有可能因为丢包而断掉,于是为了丢包重发,以及重新建立连接等,消耗大量时间与带宽,直接的感觉就是网速很慢。所以这个时候还需要做一个事情,就是把C对B的欺骗的校正过来。C发给B的是大量错误的ARP响应包,把原来正确的给淹没掉了,那么要纠正过来也很简单,那就是发送比C更多的正确的ARP响应包,把C的错误的包给淹没掉就行了。现在有不少ARP防火墙开始支持到第二个层次了。

     根据以前原理,用java写了个简单的ARP防火墙,利用的是jpcap(包装了winpcap,libpcap),有兴趣的同学可以了解一下下面的核心代码。

public class IpMacMap {

    private String ip;
    private byte[] mac;

    public IpMacMap() {
    }

    public IpMacMap(String ip, byte[] mac) {
        this.ip = ip;
        this.mac = mac;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public byte[] getMac() {
        return mac;
    }

    public void setMac(byte[] mac) {
        this.mac = mac;
    }
}
private static EthernetPacket getEthernetPacket(byte[] senderMac, byte[] targetMac) {
        EthernetPacket packet = new EthernetPacket(); // 创建一个以太网头
        packet.frametype = EthernetPacket.ETHERTYPE_ARP; // 选择以太包类型
        packet.dst_mac = targetMac;
        packet.src_mac = senderMac;
        return packet;
    }

 

private static ARPPacket getArpPacket(IpMacMap sender, IpMacMap target, short arpType) throws UnknownHostException {
        ARPPacket packet = new ARPPacket();
        packet.hardtype = ARPPacket.HARDTYPE_ETHER; // 选择以太网类型(Ethernet)
        packet.prototype = ARPPacket.PROTOTYPE_IP; // 选择IP网络协议类型
        packet.operation = arpType;
        packet.hlen = 6; // MAC地址长度固定6个字节
        packet.plen = 4; // IP地址长度固定4个字节
        packet.target_hardaddr = target.getMac();
        packet.target_protoaddr = InetAddress.getByName(target.getIp()).getAddress();
        packet.sender_hardaddr = sender.getMac();
        packet.sender_protoaddr = InetAddress.getByName(sender.getIp()).getAddress();
        return packet;
    }

 

 

    private static ARPPacket createAntiARPPacket() throws UnknownHostException {
        IpMacMap source = new IpMacMap();
        source.setIp(device.addresses[0].address.getHostAddress());//本机IP地址
        source.setMac(device.mac_address);//本机mac地址
        IpMacMap target = new IpMacMap();
        target.setIp(getwayIPAddress);//网关的IP
        target.setMac(getewayMacAddress.getMacAddress());//网关的mac地址
        ARPPacket antiSpoofPacket = getArpPacket(source, target, ARPPacket.ARP_REPLY);
        antiSpoofPacket.datalink = getEthernetPacket(source.getMac(), target.getMac());
        return antiSpoofPacket;
    }

posted on 2010-08-24 23:37  飞天老鼠  阅读(911)  评论(1编辑  收藏  举报

导航