1. 程式人生 > >Qt:Qt實現飛秋攔截助手—Mac地址掃描器

Qt:Qt實現飛秋攔截助手—Mac地址掃描器

Qt實現飛秋攔截助手—Mac地址掃描器

前言

準備好了就開幹,利用業餘時間,開始著手寫 飛秋攔截助手,按照Qt:Qt實現飛秋攔截助手—介紹 中的4步驟來走。4步驟中,Mac地址掃描器是第一步,因為網路傳輸中 最底層協議 就是網絡卡層,得到了區域網中的所有IP和對應的實體地址 才會有攻擊的目標,攔截的物件。後面的ARP欺騙和飛秋訊息攔截和飛秋欺騙會在這個基礎上新增。

效果

什麼都不說,先看看MAC地址掃描器的效果。1.2本機無線網絡卡,1.6是虛擬機器,1.4,1.3是2臺手機,1.1是閘道器。
在這裡插入圖片描述

關於網絡卡資訊

為啥要讓使用者選擇網絡卡呢?因為一個主機可能有多個網絡卡,所以上網的網絡卡不定,給使用者選擇。本機網絡卡資訊 怎樣獲取呢?請參考:

C/C++:Windows程式設計—程式碼獲取本地所有網絡卡資訊(網絡卡描述,IP地址,子網掩碼,MAC地址)

關於廠商MAC

這裡為了方便知道聯網的裝置的一些資訊,所以加了個網絡卡廠商,每臺聯網裝置的實體地址的前3個位元組 表示的是 生產該網絡卡的廠商,如何獲取呢?http://standards.ieee.org/develop/regauth/oui/oui.txt 這個網站是最新的網絡卡廠商,這裡更新廠商mac,就是從這裡去下載廠商列表,然後解析成json檔案,從而獲取裝置的網絡卡廠商。

關於Mac地址掃描器原理

這個仍然得從協議說起,因為網路協議是人為規定好的,必須遵守。在網路介面層中有一個ARP協議,叫做地址解析協議,比如說A 主機 想要和 C 主機通訊,必須知道C主機的實體地址,主機A 就需要 傳送一個ARP請求包,傳送給C 然後C收到後 會給A回一個ARP應答包 會攜帶A的實體地址,這樣主機A 的ARP快取中就會存放 主機C的實體地址,下次傳送資訊的時候後,就會在乙太網首部 填寫主機C的實體地址,就 可以找到目的主機了。
那麼我們給區域網的每個IP都發送一個ARP請求包,那麼他們給了應答包了 我們不就得到了所有的IP對應的Mac地址了。


來,我們談下具體細節了。

ARP報文結構

我們先ARP報文結構
在這裡插入圖片描述
我們程式碼中當然得按照報文結構進行組包。結構體設定如下:
這裡我們欄位型別最長的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報文

Windows API中沒有提供介面直接操作網絡卡層的方法,最底層協議包 操作也只是到 網路層,那麼怎麼辦呢?我也是後面發現Windows API 實現不了 才去找的,使用 WinPcap 庫可以實現ARP報文的傳送和接受。WinPcap中文技術文件

關於使用WireShark抓包工具分析

這工具,博主在大學用過,當時覺得好難好難,這次寫這個軟體,遇到問題了必須分析報文,去用了下,還挺好使的,我們開發肯定不是一下就把報文包 組好然後 就成功的傳送了,中間肯定會存在問題,這是需要結合抓包工具配合分析了。下面是成功傳送的 ARP報文和ARP應答報文圖。
在這裡插入圖片描述

核心程式碼

傳送APR請求報文程式碼

// 傳送ARP報文
void ArpSendThread::run()
{
    if( this->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,&ethBroadcastAddr,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<uint8_t*>(&(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<uint8_t*>(&(ipAddr)),package.arpBody.destIpAddr,4);
        int ret = pcap_sendpacket(this->mAdapterHandle,reinterpret_cast<unsigned char*>(&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<QString,QString> 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<const ArpPackage*>(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();
}

完整程式碼

總的程式碼還是挺多的,需要完整工程的點這裡進行下載