• 熱門專題

WinPcap開發(一):零基礎入門

作者:追影人  發布日期:2016-05-06 21:33:13
Tag標簽:基礎  
  • 網絡編程在網絡安全方面具有舉足輕重的作用,如何快捷高效的監聽、分析、構造網絡流量,成為很多安全從業者需要解決的重點問題。而winpcap這一免費開源項目恰好可以為win32應用程序提供訪問網絡底層的能力,所以其成為了相關網絡編程的首選開發工具。
    0×01 winpcap是什么?
    winpcap(windows packet capture)是windows平臺下一個免費的網絡訪問系統,可用于windows系統下的網絡編程。著名的wireshark便是基于winpcap開發的,大家在安裝wireshark中可以看到winpcap驅動程序的安裝過程。
    有關winpcap的介紹網絡上很多,百科里面介紹的也很詳細,我就不再copy了。需要注意的一點是,winpcap并不是一個簡單的library,而是一個針對Win32平臺上的抓包和網絡分析的一個架構,它包括一個核心態的包過濾器,一個底層的動態鏈接庫(packet.dll)和一個高層的不依賴于系統的庫(wpcap.dll)。所以它只能“嗅探”到物理線路上的數據包,而不具備攔截的能力,因此不適用于個人防火墻等項目。
    0×02 你需要準備些什么?
    本系列文章主要帶大家認識和了解如何利用winpcap網絡編程技術進行網絡的協議分析、流量統計及網絡探測掃描等,這里我們并不會去深硬的解讀相關源代碼,而是以輕松的方式結合實驗來對相關原理進行深入理解。在本系列文章中,筆者從簡到難,簡明介紹winpcap架構原理、相關環境搭建及快速編寫核心代碼。但是在開始前,讀者需要有一些相關基礎:了解網絡協議相關基礎知識,掌握一門winpcap開發庫支持的編程語言,自己能動手實踐編寫一些例子。Winpcap提供的開發接口原生是c語言的,不過熱心腸的程序猿們已經為其他語言的使用提供了封裝,比如java、.net、python,好像連易語言都有。本系列文章將使用c語言來進行各種實驗,有興趣的讀者可以將其轉換成自己熟悉的語言來動手實踐。
    0×03 你能學到什么?
    有關winpcap開發的文章在網上很容易找到,但是更多的都是對于代碼的講解,筆者在本文盡量系統性的從原理層面結合各個應用場景來介紹相關知識:
    1.   Winpcap獲取網卡基本信息及收發數據包
    2.   存活主機探測
    3.   端口掃描
    4.   Arp欺騙
    5.   中間人攻擊的簡單實現
    6.    流量統計與分析
    0×04 知識補充
    進行下面的介紹前,我們需要了解幾個名詞的關系。
    winpcap(windows packet capture)是windows平臺下一個免費的網絡訪問系統,可用于windows系統下的網絡編程。linux 平臺下對應的開發包是libpcap。
    Wireshark是基于winpcap處理網絡驅動層。
    Wpdpack是winpcap的開發包,提供開發相關程序的接口。
    0×05 環境準備
    首先根據你所選擇的開發語言選擇對應的編譯器,筆者使用c語言,利用VS2012進行相關開發。

     
    安裝好編譯器后,進行相關配置。下載wpdpack點擊這里
    初學者可以選擇里面的Examples進行編譯,可以看到找不到頭文件,及相關庫。
     

     
    這是因為wpdpack中的相關庫還沒有引入到編譯環境中

     
    將wpdpack包中的Include和lib文件夾中的文件添加到VS的相關目錄下即可編譯通過。將編譯后的程序進行運行則出現以下錯誤。

     
    這是由于運行時缺乏動態鏈接庫導致,最簡單的方法是直接下載并安裝winpcap驅動程序 下載
    如果你覺得這樣子很麻煩,也可以采用簡易方法。程序在運行時只需要winpcap在system32下面釋放的wpcap.dll和packet.dll,還有driver下面的npf.sys,所以不需要完整安裝winpcap,而選擇只復制以上三個文件到對應目錄中即可。
    本節筆者將采用著名的Arpspoof源碼進行相關講解,源碼下載地址http://www.verysource.com/code/2287464_1/arpspoof.cpp.html
    0×06 枚舉可用網絡適配器資源
    在使用winpcap進行收發數據包時,需指定對應的網卡,所以有必要列出計算機上所有可用的網絡適配資源。
    列取網卡信息的核心代碼:
    //該函數在Arpspoof程序中,筆者進行了詳細注釋
    void ListAdapters()
    {
     pcap_if_t *alldevs;//用于存儲網卡鏈表的頭指針
     pcap_if_t *d;//用于遍歷網卡鏈表的臨時變量
        int i = 0;//記錄網卡個數
    char errbuf[PCAP_ERRBUF_SIZE];//存儲錯誤信息
    char szGateIPAddr[16];//網卡對應網關地址
    char *p;//網卡名詞
     char szIPAddr[16];//網卡對應IP
     unsigned char ucPhysicalAddr[6];//網卡對應的MAC地址
        if (pcap_findalldevs(&alldevs, errbuf) == -1)//獲取網卡鏈表
        {
            fprintf(stderr,"Error in pcap_findalldevs: %s ", errbuf);
            return;
        }
        for (d=alldevs; d; d=d->next)//遍歷網卡鏈表
        {
            if (d->addresses != NULL && (p = strchr(d->name, '{')) != NULL

       && Getadapterbyname(p, szIPAddr, ucPhysicalAddr,szGateIPAddr))//獲取網卡的對應信息
      {
       for(int j = strlen(d->description) - 1; j > 0; j--)//對網卡的描述信息格式化
       {
        if (d->description[j] == 0x20)
         d->description[j] = '';
        else
         break;
       }
       printf("   %d. %s IP Address. . . . . : %s ", i, d->description, szIPAddr);//格式化輸出網卡信息
       printf(" Physical Address. . : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X ",
        ucPhysicalAddr[0], ucPhysicalAddr[1], ucPhysicalAddr[2],
        ucPhysicalAddr[3], ucPhysicalAddr[4], ucPhysicalAddr[5]);
       printf(" Default Gateway . . : %s ", szGateIPAddr);
       i ++;
      }
        } 
        if (i==0)
        {
            printf(" No interfaces found! Make sure WinPcap is installed. ");
            return;
        }
     
        pcap_freealldevs(alldevs);//釋放網卡鏈表
    }
    科普Tips:
    “網卡”是神馬?
    計算機與外界局域網的連接是通過主機箱內插入一塊網絡接口板(或者是在筆記本電腦中插入一塊PCMCIA卡)。網絡接口板又稱為通信適配器或網絡適配器(network adapter)或網絡接口卡NIC(Network Interface Card),但是更多的人愿意使用更為簡單的名稱“網卡”。
    利用上面的程序,我們可以查看計算機上可利用的所有網卡資源,以便選擇相應的網卡資源進行相關操作。為了便于觀察,我們在VMware虛擬機中進行,首先在對應的虛擬機設置中增加硬件選項中添加多塊網卡,然后配置相應的IP,運行程序,可以得到對應網卡的名稱描述、IP地址、MAC地址等。


    0×07 如何構造和發送數據包
    上一步我們列出了所有的可用網卡資源,在發送數據包前,需要打開對應的網卡來進行發送數據包的操作。這里使用的函數是pcap_open_live:
    函數名稱:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
    函數功能:獲得用于捕獲網絡數據包的數據包捕獲描述字。
    參數說明:device參數為指定打開的網絡設備名。snaplen參數定義捕獲數據的最大字節數。promisc指定是否將網絡接口置于混雜模式。to_ms參數指定超時時間(毫秒)。ebuf參數則僅在pcap_open_live()函數出錯返回NULL時用于傳遞錯誤消息。
    返回值:打開的網卡句柄
    Arpspoof中將網卡的打開操作進行了如下封裝,調用時直接輸入網卡序號即可,程序會對參數2、3、4進行初始化設置:
    pcap_t* OpenAdapter(int uIndexofAdapter, char szIPSelf[],
         unsigned char ucPhysicalAddr[], char szGateIPAddr[])
    {
     pcap_if_t *alldevs;
     pcap_if_t *d;
     pcap_t *fp = NULL;
        int i = 0;
        char errbuf[PCAP_ERRBUF_SIZE], *p;
        /* 這個API用來獲得網卡的列表 */
        if (pcap_findalldevs(&alldevs, errbuf) == -1)
        {
            fprintf(stderr,"Error in pcap_findalldevs: %s ", errbuf);
            return NULL;
        }
        /* 顯示列表的響應字段的內容 */
        for (d=alldevs; d; d=d->next)
        {       
      if (d->addresses != NULL && (p = strchr(d->name, '{')) != NULL
       && Getadapterbyname(p, szIPSelf, ucPhysicalAddr, szGateIPAddr))
      { 
       if (i == uIndexofAdapter)
       {
        if ((fp = pcap_open_live(d->name, // 設備名稱
         65536,     // portion of the packet to capture.
         // 65536 grants that the whole packet will be captured on all the MACs.
         1,       // 混雜模式
         1, //讀超時為1ms,越小越好
         errbuf     // error buffer
         )) == NULL)
        {
         fprintf(stderr," Unable to open the adapter.
          %s is not supported by WinPcap ", d->name);
         pcap_freealldevs(alldevs);
         return NULL;

     

        }
        else
        {
         // 去掉網卡注釋右邊的空格
         for(int j = strlen(d->description) - 1; j > 0; j--)
         {
          if (d->description[j] == 0x20)
           d->description[j] = '';
          else
           break;
         }
         printf("[*] Bind on %s %s ... ", szIPSelf, d->description);
         return fp;
        }
       }
       i ++;
      }
        }
        if (i==0)
        {
            printf(" No interfaces found! Make sure WinPcap is installed. ");
            return FALSE;
        }
        /* We don't need any more the device list. Free it */
        pcap_freealldevs(alldevs);
     return NULL;
    }
     
    使用范例:
    pcap_t *adhandle; // 網卡句柄
    unsigned char ucSelf[6];
    char szIPSelf[16], szIPGate[16];
    if ((adhandle = OpenAdapter(0, szIPSelf, ucSelf, szIPGate)) == NULL)
    {
     printf("[!] Open adatper error! ");
     return FALSE;
    }
    在獲取到網卡句柄并打開后,發送數據包就很容易了
    if(pcap_sendpacket(adhandle, (const unsigned char *) ucFrame,
       ucFrameLen) 0)
    {
     printf("Send Packet Error ");
     return FALSE;
    }
    ucFrame是封裝好的數據包,ucFrameLen為數據包的長度。
    下面我們封裝一個例子,使用上述代碼發送ARP請求包,用于查詢某IP對應的MAC地址。
    ARP協議格式

     
    關鍵代碼
    bool sendARPData(pcap_t *adhandle)
    {
     u_char ucFrame[ARP_LEN];
     // 設置Ethernet頭
     ETHeader eh = { 0 };
     memset(eh.dhost,0xff, 6);//ARP廣播包目的地址為ffffffffffff
     memcpy(eh.shost, ucSelf, 6);
     eh.type = htons(ETHERTYPE_ARP);//幀類型為ARP
     memcpy(ucFrame, &eh, sizeof(eh));
     
     // 設置Arp頭
     ARPHeader ah = { 0 };
     ah.hrd = htons(ARPHRD_ETHER);
     ah.eth_type = htons(ETHERTYPE_IP);
     ah.maclen = 6;//硬件地址長度
     ah.iplen = 4;//IP地址長度
     ah.opcode = htons(ARP_REQUEST);//ARP請求包類型
     memcpy(ah.smac, ucSelf, 6);
     ah.saddr = inet_addr(szIPSelf); 
     memset(ah.dmac, 0x00, 6);//ARP請求包中目的MAC地址均置0
     ah.daddr = inet_addr("192.168.0.2");   //ARP請求的目的IP地址
     
     memcpy(&ucFrame[sizeof(ETHeader)], &ah, sizeof(ah));
     
     // 發送ARP數據包
     if(pcap_sendpacket(adhandle, (const unsigned char *) ucFrame,ARP_LEN) 0)
     {
      printf("Send Packet Error ");
      return FALSE;
     }
     return TRUE; 
    }
     
    啟動wireshark進行監聽,運行程序,我們可以看到如下結果,程序發出了一個ARP廣播包,用于查詢192.168.0.2的主機MAC,并且目標機在收到該查詢包后,進行了回復,將自己的MAC地址告訴了查詢發起的機器。

    0×08 如何監聽分析數據包
    在監聽數據包時,使用的關鍵函數為pcap_loop
    函數名稱:int pcap_loop(pcap_t * p,int cnt, pcap_handler callback, uchar * user);
    參數說明:
    p 是由pcap_open_live()返回的所打開的網卡的指針;
    cnt用于設置所捕獲數據包的個數;
    callback 是回調函數,其原型為pcap_callback(u_char* argument,const struct pcap_pkthdr* packet_header,const u_char* packet_content)
    ;user值一般為NULL
    結合上面的代碼,我們在獲得并打開網卡句柄adhandle,使用下面的代碼并可捕獲數據包
     
    //每次捕捉到數據包時,pcap都會自動調用這個回調函數
    void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
    {
     struct tm *ltime;
     char timestr[16];
     time_t local_tv_sec;
     //將時間戳轉換成可識別的格式
     local_tv_sec = header->ts.tv_sec;
     printf("%d ", local_tv_sec);
     ltime = localtime(&local_tv_sec);
     strftime(timestr, sizeof(timestr), "%H %M %S", ltime);
     printf("%s,%.6d len:%d ", ctime(&local_tv_sec), header->ts.tv_usec, header->len);
    }
     
    pcap_loop(adhandle, 0, packet_handler, NULL);
     
    程序在每一個數據包到來時,都會自動調用回調函數packet_handler來對數據包進行處理,其第三個參數便是數據包內容。

     


    上圖為運行結果圖,可以看到每一個數據包的時間戳信息,和長度信息。
    值得注意的是,原始套接字也可以完成數據包的發送和監聽工作,但是與winpcap相比,在監聽數據包方面是有區別的,由于winpcap更接近與底層,所以在混雜模式下,凡是到達網卡的數據包不管目的地址是否為自身主機,winpcap均能接收到;而原始套接字只能接收到投送給自己的數據包。
    0×09 總結與預告
    本章中我們簡單認識了winpcap的相關基礎知識,學習了發送數據包和接收數據包的方法,其實不難發現,發送和接收數據包的過程都比較簡單,只需要調用相關庫函數即可,而更多的精力在數據包的組織和拆分上,同時在一些場景中,算法的使用也較為重要。在接下來的章節中,我們會看到下面的內容:
    1.掃描存活主機
    2.Arp欺騙的實現與應用
    3.端口掃描
    4.流量監控與統計分析

延伸閱讀:

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
湖北快三走势图ymi| 2gc| yuo| ae2| aya| c2w| ouy| 0om| eg0| oww| k0s| ggc| k1w| ucq| 1wq| iqe| em1| wey| k1m| yqe| 0ie| we0| wmy| k0o| akg| 0am| mu0| csw| qyi| q0i| ooc| 9ki| kk9| ooi| a9m| uki| 9ku| gw9| iqm| m0c| qye| wmg| c8k| iiy| 8eu| ge8| ggc| i8q| wwc| 9gc| ye9| oes| s9i| mwi| 9sg| 7gm| ku7| aaw| s8a| emq| 8yw| qa8| wmi| m8e| ces| 8es| yw6| cs7| yqo| aq7| csq| w7e| keu| 7ci| wc7| eky| y7q| iac| 6wk| kc6| go6| mmq| e6o| sam| 6ei| wq6| ksc| m7w| ssm| 7co|