主页 > mac电脑教程 >

Qt:Qt实现飞球拦截助手-Mac地址扫描器

Qt实现飞球拦截助手——Mac地址扫描仪前言

准备好后,我们就去做吧。闲暇之余,开始写飞球拦截助手,按照Qt中的4个步骤:飞球拦截助手的Qt实现-介绍。 4步中qt平台 mac,Mac地址扫描器是第一步,因为网络传输的最低层协议是网卡层。只有获取了局域网内的所有IP和对应的物理地址,才会有攻击和拦截的目标。在此基础上增加以下ARP欺骗和飞球消息拦截和飞球欺骗。

效果

话不多说,先看看MAC地址扫描器的效果。 1.2本地无线网卡,1.6为虚拟机,1.4,1.3为2部手机,1.1为网关。

Qt:Qt实现飞球拦截助手-Mac地址扫描器

关于网卡信息

为什么要让用户选择网卡?由于一台主机可能有多块网卡,上网所用的网卡是不确定的,给用户一个选择。如何获取本地网卡信息?请参考:C/C++:Windows编程——获取本地所有网卡信息的代码(网卡描述、IP地址、子网掩码、MAC地址)

关于制造商MAC

为了方便了解联网设备的一些信息,这里添加了一个网卡厂商。每个联网设备的物理地址的前 3 个字节表示网卡的制造商。如何获得?这个网站是最新的网卡厂商,这里更新厂商mac,就是从这里下载厂商列表,然后解析成json文件得到设备的网卡厂商。

关于Mac地址扫描器的原理

这还是得从协议说起,因为网络协议是人为规定的,必须遵守。在网络接口层qt平台 mac,有一个ARP协议叫做地址解析协议。例如,如果主机A要与主机C通信,就必须知道主机C的物理地址,主机A需要发送一个ARP请求包,发送给C,然后C接收。到达后,会向A发回一个ARP回复包,里面会携带A的物理地址,这样主机C的物理地址就会保存在主机A的ARP缓存中。 下次发送报文时,主机C将填入以太网报头。可以通过物理地址找到目的主机。

然后我们向局域网中的每个IP发送一个ARP请求包,然后他们都给出了一个回复包。我们不会得到所有IP对应的Mac地址。

来吧,让我们谈谈细节。

ARP 报文结构

先说ARP报文结构

Qt:Qt实现飞球拦截助手-Mac地址扫描器

当然,我们的代码要按照消息结构进行打包。结构设置如下:

这里,我们的字段类型最长的 uint16_t 是 2 个字节。碰巧我们的数据包是偶数个字节。根据字节对齐原则,数据包后的结构为42字节。注意我们以太网的目的地址是二进制全1,即广播地址。接收主机必须无条件接受并响应。这是每个人都必须遵守的约定。还有字节序问题,字节序问题只存在于2个字节及以上。

// 以太网首部 12byte
typedef struct _ethhead{
    // 以太网目地址 6byte
    uint8_t destEthAddr[6];
    // 以太网源地址 6byte
    uint8_t srcEthAddr[6];
    // 帧类型 2byte ,ARP请求或应答
    uint16_t frameType;
}EthHead;
// ARP请求和应答数据结构 28byte
typedef struct _arpstruct{
    // 硬件地址类型,1 表示以太网地址
    uint16_t hardType;
    // 映射的协议地址类型,0x0800 表示IP地址
    uint16_t protocolType;
    // 硬件地址长度
    uint8_t hardLen;
    // 协议地址长度
    uint8_t protocolLen;
    // 操作字段,ARP请求 1,ARP 应答 2,RARP请求 3,RARP应答 4
    uint16_t op;
    // 发送到以太网地址
    uint8_t srcEthAddr[6];
    // 发送端IP地址
    uint8_t srcIpAddr[4];
    // 目的端以太网地址
    uint8_t destEthAddr[6];
    // 目的端IP地址
    uint8_t destIpAddr[4];
}ArpStruct;
// 以太网ARP请求或应答数据包 结构 42byte
typedef struct _arppackage{
    EthHead ethHead;
    ArpStruct arpBody;
}ArpPackage;

关于Windows下如何发送ARP包

Qt:Qt实现飞球拦截助手-Mac地址扫描器

核心代码发送APR请求消息代码

Qt:Qt实现飞球拦截助手-Mac地址扫描器mAdapterHandle == nullptr){ qDebug() << "网卡设备没有开启"; return; } char tmp[18] = {0}; Utils::macToHexString(this->mMacAddr,tmp); qDebug() <<"begin:" << tmp; // 构造ARP请求包 ,2字节及以上的 存在大小端对齐问题,需要转换为网络字节序 ArpPackage package; // 以太网 头部 uint64_t ethBroadcastAddr = 0xffffffffffff;// 6字节 以太网 广播地址,局域网主机无条件接受 memcpy(package.ethHead.destEthAddr,ðBroadcastAddr,6); memcpy(package.ethHead.srcEthAddr,this->mMacAddr,6); package.ethHead.frameType = htons(0x0806); memset(tmp,0,18); Utils::macToHexString(this->mMacAddr,tmp); qDebug() <<"origin:" << tmp; memset(tmp,0,18); Utils::macToHexString(package.ethHead.srcEthAddr,tmp); qDebug() <<"now:" << tmp; // 构造ARP请求体内容 package.arpBody.hardType = htons(1);// 以太网地址 package.arpBody.protocolType = htons(0x0800); // IP地址 package.arpBody.hardLen = 6; package.arpBody.protocolLen = 4; package.arpBody.op = htons(1); memcpy(package.arpBody.srcEthAddr,this->mMacAddr,6); // 硬件厂商 Mac地址 http://standards-oui.ieee.org/oui/oui.txt Utils::htonN(reinterpret_cast(&(this->mCurIPAddr)),package.arpBody.srcIpAddr,4); memset(package.arpBody.destEthAddr,0,6); int i = 1; // 往当前局域网中所有IP发送 ARP报文 for(uint32_t ipAddr = mNetworkAddr+1; ipAddr < mBroadcastAddr; ipAddr++,i++){ if(this->isScan == false) break; if( ipAddr == this->mCurIPAddr) continue; struct in_addr addr; addr.S_un.S_addr = htonl(ipAddr); qDebug() << inet_ntoa( addr); Utils::htonN(reinterpret_cast(&(ipAddr)),package.arpBody.destIpAddr,4); int ret = pcap_sendpacket(this->mAdapterHandle,reinterpret_cast(&package),42); if( ret != 0){ qDebug() << inet_ntoa( addr) << " 发送失败!" ; } emit sendOne(i); Sleep(100); } // 关闭设备 pcap_close(this->mAdapterHandle); // arp数据包发送完毕,通知主线程 emit sendDone(); }

接收ARP回复消息代码

void ArpAcceptThread::run(){
    if( this->mAdapterHandle == nullptr){
        qDebug() << "网卡设备没有开启";
        return;
    }
    int res;
    struct tm *ltime;
    char timestr[16];
    struct pcap_pkthdr *header;
    const u_char *pkt_data;
    time_t local_tv_sec;
    struct bpf_program fcode;
    QMap info;
    // 表达式 (arp[16:2]&0x00010!=0) and (dst host 192.168.1.2)
    // (arp[6:2]&0x0002!=0) 过滤ARP应答
    // http://www.ferrisxu.com/WinPcap/html/group__language.html 过滤表达
    QString exp = QString("(arp[6:2]&0x0002!=0)");
    // compile the filter
    if (pcap_compile(this->mAdapterHandle, &fcode, exp.toStdString().c_str() , 1, 0) < 0)
    {
        qDebug() << "pcap_compile error:" <<  pcap_geterr(this->mAdapterHandle);
    }
    // set the filter
    if (pcap_setfilter(this->mAdapterHandle, &fcode) < 0)
    {
        qDebug() << "pcap_setfilter error";
    }
    // 获取数据包
    while((res = pcap_next_ex( this->mAdapterHandle, &header, &pkt_data)) >= 0){
        if( this->isAccept == false)
            break;
        if(res == 0)
            // 超时时间到
            continue;
        // 将时间戳转换成可识别的格式
        local_tv_sec = header->ts.tv_sec;
        ltime=localtime(&local_tv_sec);
        strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
        printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
        fflush(stdout);
        const ArpPackage *package = reinterpret_cast(pkt_data);
        struct in_addr addr ;
        memcpy(&addr.S_un.S_addr,package->arpBody.srcIpAddr,4);
        info["ip"] = inet_ntoa(addr);
        qDebug() << "ip=" << inet_ntoa(addr);
        // 从视觉上看 macCh已经转为本地字节序
        char macCh[18] = {0};
        Utils::macToHexString(package->arpBody.srcEthAddr,macCh);
        qDebug() << "mac=" << macCh;
        info["mac"] = QString(macCh);
        emit acceptArp(info);
    }
    if(res == -1){
        qDebug() << "Error reading the packets: "<< pcap_geterr(this->mAdapterHandle);
    }
    emit acceptDone();
}

完整代码

总代码还是蛮多的。如果您需要一个完整的项目,请点击此处下载。