zoukankan      html  css  js  c++  java
  • Promiscuous Mode

    简介

    Monitor mode 与 promiscuous mode 比较

    这是在网卡上的的两个特殊的模式,简而言之,都是将网卡的过滤器关闭。

    • Monitor mode

    这是我们常常提到的sniffer mode。它用于无线网络中,无线网卡开启监听模式,监听空气中的所有数据包,其中它还可以切换channel。如果设置得当,可以同时监控所有信道的帧(切换式,或者同时多个网卡监听)。在这个模式下面,STA是没有连接到AP的。除了能够得到数据包之外,还可以得到控制帧和管理帧。对于debug 802.11的问题(omnipeek or wireshark in linux)或者攻击一个802.11的网络(aircrack、reaver),常常会使网卡进入到这个模式。此文重点在于promiscuous mode,802.11的monitor mode不再赘述。

    • Promiscuos mode

    不处于promiscuous mode的网卡只会收取DA会自身的数据包或者BC/MC的数据包。在promiscuos mode下面,网卡会收到DA不是自身的包,在进入这个模式的时候,常常会开启一个raw socket,来接收网卡传递上来的数据。

    打开promiscuous mode

    翻阅libpcap的源码,可以整理出这两种方式,可以打开promiscuous mode:

    (activate_new) – new way

    1.Try to open a packet socket using the new kernel PF_PACKET interface
    2.Select promiscuous mode on

    struct packet_mreq  mr; memset(&mr, 0, sizeof(mr));
    mr.mr_ifindex = handlep->ifindex;
    mr.mr_type    = PACKET_MR_PROMISC;
    sock_fd =  socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr))
    

    setsockopt 原型如下:

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

    对应于内核的 /include/net/sock.h

    (setsockopt)(struct sock sk, int level, int optname, char __user *optval, unsigned int optlen);

    net/packet/af_packet.c 里面有对应的操作:

    static int
    packet_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
    {
        struct sock *sk = sock->sk;
        struct packet_sock *po = pkt_sk(sk);
        int ret;
    
        if (level != SOL_PACKET)
            return -ENOPROTOOPT;
    
        switch (optname) {
        case PACKET_ADD_MEMBERSHIP:
        case PACKET_DROP_MEMBERSHIP:
        {
            struct packet_mreq_max mreq;
            int len = optlen;
            memset(&mreq, 0, sizeof(mreq));
            if (len  sizeof(mreq))
                len = sizeof(mreq);
            if (copy_from_user(&mreq, optval, len))
                return -EFAULT;
            if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address)))
                return -EINVAL;
            if (optname == PACKET_ADD_MEMBERSHIP)
                ret = packet_mc_add(sk, &mreq);
            else
                ret = packet_mc_drop(sk, &mreq);
            return ret;
        }
    

    调用流程:

    packet_mc_add -> packet_dev_mc -> dev_set_promiscuity(net/core/dev.c)

    dev_set_promiscuity的定义如下:

    int dev_set_promiscuity(struct net_device *dev, int inc)
    {
        unsigned int old_flags = dev->flags;
        int err;
    
        err = __dev_set_promiscuity(dev, inc, true);
        if (err <0 return err if dev->flags != old_flags)
            dev_set_rx_mode(dev);
        return err;
    }
    
    __dev_set_promiscuity =>
    dev->flags |= IFF_PROMISC;
    dev_change_rx_flags(dev, IFF_PROMISC);
    ops(net_device_ops)->ndo_change_rx_flags(dev, flags);
    
    dev_set_rx_mode(dev); ==>
    ops(net_device_ops)->ndo_set_rx_mode(dev);
    

    驱动基本上只实现了ndo_set_rx_mode

    选择一个Ethernet驱动作为例子:

    const struct net_device_ops ei_netdev_ops = {
         ....
        .ndo_open        = ei_open,
        .ndo_stop        = ei_close,
        .ndo_start_xmit        = ei_start_xmit,
        .ndo_set_rx_mode    = ei_set_multicast_list,
    };
    
    ei_set_multicast_list -> 
    __ei_set_multicast_list -> 
    do_set_multicast_list
    
    static void do_set_multicast_list(struct net_device *dev)
    {
        ....
        if (dev->flags&IFF_PROMISC) {
            memset(ei_local->mcfilter, 0xFF, 8);
            ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
        }
    }
    

    这边可以看到驱动设置了EN0_RXCR,含义猜测是RX config register,将RX的接收状态设置为accept all。所以说,设置设备进入promiscuous mode,实际上是设置硬件寄存器ndo_set_rx_mode,需要设备的驱动支持。

    (activate_old) – old way

    struct ifreq ifr;
    ifr.ifr_flags |= IFF_PROMISC;
    ioctl(handle->fd, SIOCSIFFLAGS, &ifr)
    

    这种开启方法,多个开启混杂模式的程序可能会干扰,容易出问题,不推荐使用
    在 net/core/dev_ioctl.c

    => dev_ifsioc
    => dev_change_flags
    => __dev_change_flags
    => dev_set_rx_mode

    下面的路径就和上一种方法一样了,不再赘述。可以了解到,在底层的操作,两个做法都是一样的,都是关闭了网卡的过滤器。

    如何判断一个机器位于promiscuous mode

    既然网卡驱动放行了所有的数据包并且将数据包上传TCPIP协议栈,那么这里可以使用一个技巧。将进入promiscuous mode的主机设为A。从同一网段的B主机伪造一个目标MAC地址不是A,但是目标ip地址是A的包。当正常的主机收到这个包的时候,网卡驱动或者网卡的asic就会将它丢弃,当这个主机进入promiscuous mode之后,网卡驱动放行这个包,TCPIP协议栈检查这个包的目标地址是A,所以会产生一个回应到B主机。当然了,如果主机A特意将TCPIP协议栈的收发包路径关掉,那么就无法侦测到了。

    另外,如果我伪造了一个不存在的mac地址,那么交换机就不知道这个mac地址是位于哪个端口,它也会帮忙转发到所有端口。

    比如我从B主机伪造一个错误MAC地址的ICMP包给A主机,判断A主机是否回应,这样就可以判断出它是否处于promiscuous mode。

    MACIPPayload
    局域网内没有出现过的mac地址 192.168.0.5 xxxxxxxx

    做一个测试

    测试环境: ubuntu 12.4 + win10
    测试网卡:Ethernet 网卡
    测试软件:wireshark,ICMP c语言测试程序
    两端的ip:192.168.0.4,192.168.0.5
    测试方式:直连,家用路由器LAN口转发

    下面是测试的过程。首先我在目标机器上面(win10)开启了wireshark,并且监听本地连接。这时候我伪造了一个错误的MAC DA,但是其他参数,比如对方的ip地址是填写正确的。这时候本机的wireshark可以看到对方的ICMP回应。紧接着我关闭win10上面的wireshark,ICMP回应消失。随后我又开启wireshark,又可以得到ICMP回应了。测试的时候,两台机器使用网线直连或者通过家用路由器的LAN口转发都是成功的,但是使用无线网络的时候是失败的,可能是无线网卡不支持promiscuous mode。注意到,因为是要伪造MAC地址,所以需要使用PF_PACKET来操作RAW socket

    下图是在ubuntu上面的wireshark截取的

    另外,如果两个机器直连测试,需要在windows上面设置静态ip地址(192.168.0.5/24),在linux机器上面关闭界面上的网络连接,并且输入测试网络是否连通:

    tanhangbo@ThinkPad:~$ sudo ifconfig eth0 up
    tanhangbo@ThinkPad:~$ sudo ifconfig eth0 192.168.0.4
    tanhangbo@ThinkPad:~$ ping 192.168.0.5
    PING 192.168.0.5 (192.168.0.5) 56(84) bytes of data.
    64 bytes from 192.168.0.5: icmp_req=1 ttl=128 time=0.312 ms
    64 bytes from 192.168.0.5: icmp_req=2 ttl=128 time=0.384 ms
    64 bytes from 192.168.0.5: icmp_req=3 ttl=128 time=0.408 ms
    

    实际作用

    一般来说,进入这个模式,就是为了多收一些包,进行网络诊断。一般开启wireshark的时候就会帮你打开promiscuous mode。底层操作在libpcap里面。

    • 参考资料:
    1. libpcap和linux kernel 源码
    2. http://stackoverflow.com/questions/6666257/what-is-the-purpose-of-net-device-uc-promisc-field





  • 相关阅读:
    Asp.net的安全问题
    周末了
    GDI+ 取得文本的宽度和高度
    Family的解释
    日语:名词并列
    第一次来入住园里
    All About Floats
    smarty的基本配置
    apache:一个ip绑定多个域名的问题
    CSS Overflow属性详解
  • 原文地址:https://www.cnblogs.com/tanhangbo/p/5425382.html
Copyright © 2011-2022 走看看