关于蹭网检查的原理及实现

引言

网络十分普及的现在,几乎每家每户都用上了无线局域网, 但也时常因为路由器密码泄露或破解被别人蹭网,加之WiFi 万能钥匙等软件的流行, 越来越多人加入了蹭网大军, 也给不少小型局域网用户带来了烦恼. 目前许多安全软件厂商都在推出检查蹭网的小程序, 通过这样的程序可以十分便捷的看到哪些设备在使用局域网, 从而及时发现和采取应对措施, 为广大用户弥补了损失.

准备

笔者手里正好有一款由nisoft发布的检查蹭网的小程序, 叫做Wireless Network Watcher, 软件免费试用, 文末将给出下载地址. 本文将针对该款软件做分析. 其次是WireShark协议分析工具, 这个软件很常见, 感兴趣的话可以百度下载. 

笔者的系统仍然是Windows8.1 Pro.

操作步骤

1.打开Wireless Network Watcher, 在Advanced Options中设置合适的Network adapter(可能含有其他网络设备的网段), 笔者主机所在网段是192.168.199.*

2.打开WireShark,选择合适的网卡然后启动监控

3.启动Wireless Network Watcher的扫描

4.等待扫描结束,然后停止两个软件

5.在WireShark中进行分析

分析

从Wireless Network Watcher的扫描结果来看,除了Router之外还发现了3个设备,一个是我的主机(Your Computer),一个是我的安卓手机,还有一个是HyFi智能无线路由器.

此外,包括MAC地址在内的IP地址, 设备名称Device Name等等都获取到了.

从Wireshark的抓包结果来看, 大多数是ARP协议, 在ARP应答报文中可以得到对应在线主机的MAC地址, 程序在收到应答后有一个DNS反向解析动作, 在DNS应答中又可以得到设备的Device Name,而这个Device Name应该是存在Router中的. 过滤其他干扰协议保留ARP和DNS. 可以十分明显的发现ARP请求报文的 Target IP address是从网段中0开始,一直到255, 也就是说软件扫描了整个网段, 发送了256个ARP广播用来查找在线的主机, 然后通过得到的IP地址向路由器发送DNS反向解析请求,用来获取设备的名称.  如图所示:

这里先借一段关于ARP协议的百科:

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。其功能是:主机将ARP请求广播到网络上的所有主机,并接收返回消息,确定目标IP地址的物理地址,同时将IP地址和硬件地址存入本机ARP缓存中,下次请求时直接查询ARP缓存。地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记录在本地的ARP缓存中,这样攻击者就可以向目标主机发送伪ARP应答报文,使目标主机发送的信息无法到达相应的主机或到达错误的主机,构成一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。

---- 百度百科

通过这段引用可以了解到ARP协议的Request是一个广播,发送到网络上所有主机,然后接收应答.

关于DNS的工作原理可以参考我的另一篇文: 利用WireShark进行DNS协议分析

现在让我们来分析一个ARP的请求和应答,以及一个DNS的请求和应答.

以第一个ARP为例:

第一个包是ARP请求,第二个是ARP应答,第三个是DNS请求,第四个是DNS应答,下面依次分析:

1. ARP请求

ARP请求的关键在于目标MAC尚未知道,因此全0,注意上层协议中的目标MAC是全f, 表示一个广播, 由于不同层的协议不同, 因此含义也不同. 请求解析的地址是192.168.199.1, opcode为 0x0001代表该ARP是一个Request.

ARP报文格式不在本文的讨论范围, 其本身也比较简单. 请读者自行百度.

2. ARP应答

当ARP请求广播后, 收到请求的主机检查Target IP address是否和自己相同,相同就回应一个ARP, 注意此时不再是一个广播了,而是定向的回应发送者. Sender MAC address字段里放有我们希望得到的目标MAC地址.

主机得到来自192.168.199.1的应答后, 取出MAC并记下IP地址, 为后面的DNS反向解析做准备.

其实到此为止, 就探测到了一个在线的主机, 完成了关键的侦测任务, 下面的DNS是软件本身为了优化用户体验, 向路由器查询一下设备名称而已. 此外,如果广播出去的ARP一定时间内没有收到回应,说明所探测的主机不在线.

3. DNS请求

主机收到ARP回应后, 利用IP地址进行DNS反向解析, 注意查询IP字段在报文中是逆序存放的. 目的地是路由器.

4. DNS应答

DNS应答报文中指出Domain Name是 Hiwifi.lan 这和软件上的Device Name一致.

实现

之前直接打算用boost的asio网络库来实现ARP的收发,搞了半天发现asio的rawsocket不能自定义实现这样的底层协议,网络上关于boost实现ARP的资料几乎没有,因此考虑了windows平台下的winpcap.

首先需要自己定义好arp以及ethernet报文(帧)的标准格式,为了简便起见,成员函数全部使用inline方式, 下面两个hpp分别定义了这两种格式:

//ethernet_header.hpp

#ifndef ETHERNET_HEADER_HPP
#define ETHERNET_HEADER_HPP

#include <istream>
#include <ostream>
#include "mac_address.hpp"

// Ethernet header
//
// The wire format of an Ethernet header is:
// 0                 5                 11    13
// +-----------------+-----------------+-----+
// |destination mac  |source mac       |type |
// |XX:XX:XX:XX:XX:XX|YY:YY:YY:YY:YY:YY|ZZ:ZZ|
// +-----------------+-----------------+-----+

class ethernet_header
{
public:
    ethernet_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }

    void dst(const MacAddr &mac_address) {
        for (size_t i = 0; i < mac_address.size(); ++i) {
            rep_[0 + i] = mac_address[i];
        }
    }

    void src(const MacAddr &mac_address) {
        for (size_t i = 0; i < mac_address.size(); ++i) {
            rep_[6 + i] = mac_address[i];
        }
    }

    void type(unsigned short n) { encode(12, 13, n); }

    MacAddr dst() const {
        MacAddr mac_address;
        for (int i = 0; i < 6; ++i) {
            mac_address.push_back(rep_[0 + i]);
        }
        return mac_address;
    }

    MacAddr src() const {
        MacAddr mac_address;
        for (int i = 0; i < 6; ++i) {
            mac_address.push_back(rep_[6 + i]);
        }
        return mac_address;
    }

    unsigned short type() const { return decode(12, 13); }

    friend std::istream& operator>>(std::istream& is, ethernet_header& header)
    {
        return is.read(reinterpret_cast<char*>(header.rep_), 14);
    }

    friend std::ostream& operator<<(std::ostream& os, const ethernet_header& header)
    {
        return os.write(reinterpret_cast<const char*>(header.rep_), 14);
    }

private:
    unsigned short decode(int a, int b) const
    {
        return (rep_[a] << 8) + rep_[b];
    }

    void encode(int a, int b, unsigned short n)
    {
        rep_[a] = static_cast<unsigned char>(n >> 8);
        rep_[b] = static_cast<unsigned char>(n & 0xFF);
    }

    unsigned char rep_[14];
};

#endif // ETHERNET_HEADER_HPP
//arp_header.hpp
#ifndef ARP_HEADER_HPP
#define ARP_HEADER_HPP

#include <istream>
#include <vector>
#include <boost/asio.hpp>

#include "mac_address.hpp"

// ARP header
//
// The wire format of an ARP header is:
//
// 0               8               16                             31
// +-------------------------------+------------------------------+      ---
// |                               |                              |       ^
// |     Hardware type (HTYPE)     |    Protocol type (PTYPE)     |       |
// |                               |                              |       |
// +---------------+---------------+------------------------------+    4 bytes
// |               |               |                              |       ^
// |   Hard. len.  |  Proto. len.  |       Operation (OPER)       |       |
// |    (HLEN)     |    (PLEN)     |                              |       |
// +-------------------------------+------------------------------+    8 bytes
// |                                                              |       ^
// |                  Sender hardware address (SHA)               |       |
// |                                                              |       |
// +--------------------------------------------------------------+    14 bytes
// |                                                              |       ^
// |                  Sender protocol address (SPA)               |       |
// |                                                              |       |
// +--------------------------------------------------------------+    18 bytes
// |                                                              |       ^
// |                  Target hardware address (THA)               |       |
// |                                                              |       |
// +--------------------------------------------------------------+    24 bytes
// |                                                              |       ^
// |                  Target protocol address (TPA)               |       |
// |                                                              |       |
// +--------------------------------------------------------------+    28 bytes

class arp_header
{
public:

    arp_header(){ std::fill(rep_, rep_ + sizeof(rep_), 0); }

    //setter
    void htype(unsigned short n){ encode(0, 1, n); }

    void ptype(unsigned short n){ encode(2, 3, n); }

    void hsize(unsigned char n){ rep_[4] = n; }

    void psize(unsigned char n){ rep_[5] = n; }

    void opcode(unsigned short n){ encode(6, 7, n); }

    void sha(const MacAddr & mac){
        for (size_t i = 0; i < mac.size(); ++i)
            rep_[8 + i] = mac[i];
    }

    void spa(const boost::asio::ip::address_v4 &address){
        auto bytes = address.to_bytes();
        rep_[14] = bytes[0];
        rep_[15] = bytes[1];
        rep_[16] = bytes[2];
        rep_[17] = bytes[3];
    }

    void tha(const MacAddr& mac){
        for (size_t i = 0; i < mac.size(); ++i)
            rep_[18 + i] = mac[i];
    }

    void tpa(const boost::asio::ip::address_v4 &address){
        auto bytes = address.to_bytes();
        rep_[24] = bytes[0];
        rep_[25] = bytes[1];
        rep_[26] = bytes[2];
        rep_[27] = bytes[3];
    }

    //getter
    unsigned short htype() const { return decode(0, 1); }

    unsigned short ptype() const { return decode(2, 3); }

    unsigned char hsize() const { return rep_[4]; }

    unsigned char psize() const { return rep_[5]; }

    unsigned short opcode() const { return decode(6, 7); }

    MacAddr sha()const {
        MacAddr mac;
        for (size_t i = 0; i < 6; i++)
            mac.push_back(rep_[8 + i]);
        return mac;
    }

    boost::asio::ip::address_v4 spa() const {
        boost::asio::ip::address_v4::bytes_type bytes
            = {rep_[14], rep_[15], rep_[16], rep_[17]};
        return boost::asio::ip::address_v4(bytes);
    }

    MacAddr tha()const{
        MacAddr mac;
        for (int i = 0; i < 6; ++i)
            mac.push_back(rep_[18 + i]);
        return mac;
    }

    boost::asio::ip::address_v4 tpa() const {
        boost::asio::ip::address_v4::bytes_type bytes
            = {rep_[24], rep_[25], rep_[26], rep_[27]};
        return boost::asio::ip::address_v4(bytes);
    }

    //overloads

    friend std::istream& operator>>(std::istream& is, arp_header& header)
    {
        return is.read(reinterpret_cast<char*>(header.rep_), 28);
    }

    friend std::ostream& operator<<(std::ostream& os, const arp_header& header)
    {
        return os.write(reinterpret_cast<const char*>(header.rep_), 28);
    }

private:
    void encode(int a, int b, unsigned short n)
    {
        rep_[a] = static_cast<unsigned char>(n >> 8);//取出高8位
        rep_[b] = static_cast<unsigned char>(n & 0xff);//取出低8位
        //相当于转换字节序,把小端格式转换为网络字节序
        //例如 数 0x1234 在小端模式(Little-endian)中表示为:
        //低地址---->高地址
        //34         12
        //网络序,大端模式(Big-endian)应该是:
        //12         34
        //该函数实现这个功能
    }

    unsigned short decode(int a, int b) const
    {
        return (rep_[a] << 8) + rep_[b];
        //这个就是encode的反函数,把两个字节倒过来返回
    }
    unsigned char rep_[28];
};

#endif // ARP_HEADER_HPP

关于主机字节序(本例为小端)和网络字节序(大端)的转换过程可以参考上面代码中的注释.

实在贴不下这么多代码了,主函数代码包括所有本文涉及的hpp源代码请见下面的代码分享链接.

由于时间仓促,代码仅供学习交流,有很多遗留的问题尚未解决,但并不影响大家对整个实现过程的理解

运行结果

程序将扫描定义好的整个ip区段,发送ARP广播,然后接收响应,列出目标的MAC地址.具体实现请看下面的代码分享.

代码分享

http://www.oschina.net/code/snippet_580940_37722

时间: 2017-06-02

关于蹭网检查的原理及实现的相关文章

路由器怎么设置密码不被别人蹭网?

  在网上经常看到一些网友抱怨,家中的Wifi网络经常被人蹭网,从而导致网速变慢,但又找不到原因与对应的解决办法.于是乎,网上经常可以看到,路由器怎么设置密码不被别人蹭网?对于这个问题,其实解决起来并不难,下面本文为大家分享一些行之有效的路由器防蹭网攻略. 首先来说说,无线路由器为什么会被蹭网?大致有以下几种原因: 1.无线路由器没有设置Wifi密码 如果路由器将Wifi密码设置为空(也就是没有密码),这种情况,只要附近的手机/平板/笔记本等设备能够搜索到您家的路由器无线信号,就可以无需密码的轻

无人蹭网但是WiFi越来越慢怎么办

  在家里用WiFi上网的我们,有的时候会发现网速忽然变得很慢很卡,十分影响看剧玩游戏的心情.排查原因发现,没有人蹭网但网速依旧龟速.无奈之下打电话咨询运营商客服,得到的答复却总是千篇一律的"这边为查询到您的网络一切正常,建议您试着重启您家的无线路由器."虽然感觉可能是客服君的"忽悠",但在还是半信半疑重启了无线路由器后,惊喜发现网速还真的变得顺畅许多了. 这是什么原理呢?IT人士表示,无线路由器由处理器.内存.存储装置组成,类似一台小型的计算机.它通过网络接收并转

360蹭网神器有用吗

  360蹭网神器有用吗?与wifi万能钥匙哪个好 先不管神器不神器,能蹭网上才是王道,其实这2款软件的原理都差不多,都是通过用户分享的密码传送至云端,然后供大家享用,至于有用没用这个就说不准了,说不定你用360在这个地方不管用,但是去了下个地方又用得上,wifi万能钥匙也是如此,所以小编建议还是都装上比较好,那里能用就用谁,有备无患嘛.

无线陷阱无线钓鱼 揭露蹭网背后的杀手

"卡王"."蹭网卡"."蹭网秘笈"."简明蹭网指南"."蹭网专业户",这些时髦的词都跟"蹭网"有关.智能手机.平板电脑让无线需求暴增,于是催生了蹭网这一现象,甚至产生了相关的非法产品,比方说蹭网卡.一群追求免费上网的人士,逐渐形成了蹭网群体. 蹭网,就是利用自己周围没有设置加密验证的无线路由,偷接入别人的网络之中,达到免费上网目的.这种明显盗用他人网络资源的行为,如今却被许多蹭网爱好者称

如何鉴别是否被蹭网?

  你家的Wi-Fi被蹭了!你造吗? 有时看着视频突然卡了,或者下载速度明显下降,那么警惕了,你家的路由器可能就是被蹭网了!那么如何判断Wi-Fi是否被盗用呢? 现在很多智能路由器都自带手机APP,可以随时查看联网的设备,踢到蹭网设备也是轻而易举.但路由器不是年抛设备,很多人都还用着老古董,对于它们,应该如何查看是否被蹭网呢?又该如何规避呢?下面简单6招解决蹭网问题. 如何鉴别是否被蹭网? 第一点. 网速无缘无故变慢. 如果平时下载.浏览都很快,网速突然变慢,就要警惕. 第二点. 发现陌生设备.

无线路由防蹭网办法

俗话说"魔高一尺道高一丈",有人要挖空心思的想不花钱来蹭你的网,造成你的网速大下降,这种损人利己的事,我们这些花了钱的主儿,能答应吗?不能!那么如何来有效防止别人破解了.我来说几点办法: 1.当你感觉网速明显变慢,怀疑有人蹭网时,登录路由器服务地址,在"无线参数----主机状态"下,就会显示现在有几台主机连接你的路由器,如果就你一个电脑,可是显示有2个或更多个主机,那么,你已经被蹭啦!! 2.使用"WPA--PSK/WPA2--PSK"安全类型,

你家的Wi-Fi被蹭了!你造吗?简单6招解决蹭网问题

  现在很多智能路由器都自带手机APP,可以随时查看联网的设备,踢到蹭网设备也是轻而易举.但路由器不是年抛设备,很多人都还用着老古董,对于它们,应该如何查看是否被蹭网呢?又该如何规避呢?下面简单6招解决蹭网问题. 如何鉴别是否被蹭网? 第一点. 网速无缘无故变慢. 如果平时下载.浏览都很快,网速突然变慢,就要警惕. 第二点. 发现陌生设备. 在电脑网络上发现陌生设备基本可以肯定比的网络已经被别人盗用. 第三点. 排查IP地址. 经由管理界面经路由器上所列设备与电脑名.IP地址.硬件地址进行对比,

如何查看wifi是否被蹭网

  随着无线WIFI设备越来越多,笔记本,手机,数字电视等,无线路由器的试用量也越来越多. 很多路由器用户比较关系的路由器有几个人在用的问题,担心被蹭网,因为用的人多了,网速变慢,还存在一个隐私问题.现在我来说一个怎么查看路由器下有几个人在用 一.登陆路由器管理界面, 在浏览器中输入路由器的登录地址(相当于本地网址),一般路由器的登录地址均是192.168.1.1或者192.168.0.1,想确认路由器地址是哪个查看路由器底部上的介绍 二.输入用户名和密码, 一般路由器都是:用户名:admin

如何判断无线路由器有没有被蹭网

很多家庭用户用上了无线路由器这个"高科技"的东西,但心里却在想,有没有其他用户在无偿的使用我的无线网络呢?怎么判断有没有被蹭网?下面小编就为你分析一下: 如果您的无线路由器无线没有设置加密或者加密类型设置为WEP加密,都有可能被别人蹭网.解决方法如下: 1. 登录到路由器管理界面,点击"无线参数"--"主机状态"(如下截图). 主机状态会显示出当前连接到您路由器的主机MAC地址,据此您可以判断是否有非法用户接入. 2. 如果当前无线设置加密类型为

怎样判断自己无线网络是否被蹭网

  解决办法: 方法一.一般来说,要判断是否被蹭网,你可以装一个局域网的IP扫描软件,用软件一扫,就可以看到有哪些IP.MAC 连入了你的局域网,如果遇到陌生的IP.MAC,直接禁止了就好了. 方法二.如果对CMD的相关命令比较熟悉,无需软件也可以看到.运行CMD,输入arp(空格)-a ,这里就可以看到所有连入局域网的IP地址.你可以看看,哪些IP是陌生的,如果有的话,那么就需要在路由器中将其禁止掉.此外,为了避免再次被蹭网,建议你将无线密码设置得复杂一些.