1. 程式人生 > >一步一步開發sniffer(Winpcap+MFC)(六)千呼萬喚始出來,不抱琵琶也露面——將解析資料寫到GUI上

一步一步開發sniffer(Winpcap+MFC)(六)千呼萬喚始出來,不抱琵琶也露面——將解析資料寫到GUI上

最後一章是要將解析資料寫的GUI上,先來回顧一下GUI長什麼樣,這樣就對要在介面上寫什麼資料心中有數了,如下這兩圖:

 


可以看出,要寫在GUI上的資料主要有五個部分:

1、  引數設定:網絡卡介面、過濾項

2、  資料包捕獲列表,顯示資料包簡要資訊

3、  樹形目錄,顯示被選中的資料包頭詳細資訊

4、  文字框,顯示被選中的資料包十六進位制資訊

5、  統計資訊:各類包的數量

限於篇幅接下來不會對所有資訊是怎麼放、每個控制元件怎麼操作一一講解了。首先會簡單講解一下是如何將資料放到介面上的;同時再對一些要點:第2點資訊中如何根據資料包型別顯示不同顏色背景,第4點資訊中如何將資料包內容以格式化的方式顯示出來做一下講解。

一、控制元件寫資料的基本操作

由於我們選擇的是對話方塊的形式的介面,所以主介面只有一個,放置在主介面上的各個控制元件都可以通過主介面的this指標呼叫,並設定控制元件的值,例如:

          this->m_listCtrl.SetItemText(nItem,2,buf);

其中m_listCtrl是放置於主介面上的一個列表控制元件,至於引數是什麼意思查MSDN吧。各種控制元件上的資料也就基本通過這樣的方式呼叫

程式中我們新開了一個執行緒來處理資料,執行緒中每收到一個數據包都需要更新一下介面,這樣就可以實時看到捕獲的資料及統計資訊了,這需要我們把主介面的this指標傳遞給執行緒,如下:

         m_ThreadHandle=CreateThread(NULL,0,lixsinff_CapThread,this,0,threadCap);

執行緒處理函式原型如下:

         DWORD WINAPI lixsinff_CapThread(LPVOID lpParameter);

這裡的lpParameter就是剛剛傳遞進來的this指標了,在函式中使用如下:

         Cmcf6Dlg*pthis = (Cmcf6Dlg*) lpParameter;

其他的使用就跟前面一樣了。

有時候需要根據事件來顯示資料,那麼控制元件的事件怎麼新增呢,一個簡單的辦法就是開啟資源檢視,選擇某一控制元件,再點選“閃電”形的按鈕,就可以看到IDE已經早就為控制元件設定好各種事件了,你只需要選擇某一事件,並增加自定義函式就行了。如下圖

                  

二、兩個要點

下面兩個要點是我在顯示資料的時候遇到的實際困難,因為其他的資料都是直接將資料寫到控制元件裡就完事了,而這裡則要先做一些預處理,將這個貼出來節省大家時間。

2.1根據不同協議顯示不同顏色

從下面這幅圖可以看到,List控制元件有一個事件是NM_CUSTOMDRAW,每次有新的一行加入的時候,都觸發該事件,然後呼叫相關的處理函式進行自定義繪製,可以註冊一個該事件,程式碼見下         

   

   
voidCmcf6Dlg::OnNMCustomdrawList1(NMHDR *pNMHDR, LRESULT *pResult)
{
         //LPNMCUSTOMDRAWpNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
         LPNMLVCUSTOMDRAWpNMCD = (LPNMLVCUSTOMDRAW)pNMHDR;
         *pResult= 0;
         //TODO: 在此新增控制元件通知處理程式程式碼
         if(CDDS_PREPAINT==pNMCD->nmcd.dwDrawStage)
         {
                   *pResult= CDRF_NOTIFYITEMDRAW;
         }elseif(CDDS_ITEMPREPAINT ==pNMCD->nmcd.dwDrawStage){
                   COLORREFcrText;
                   charbuf[10];
                   memset(buf,0,10);
                   POSITION pos =this->m_localDataList.FindIndex(pNMCD->nmcd.dwItemSpec);
                   struct datapkt * local_data =(struct datapkt *)this->m_localDataList.GetAt(pos);
                   strcpy(buf,local_data->pktType);
 
                   if(strcmp(buf,"IPV6")==0)
                            crText= RGB(111,224,254);
                   elseif(strcmp(buf,"UDP")==0)
                            crText= RGB(194,195,252);                               
                   elseif(strcmp(buf,"TCP")==0)
                                     crText= RGB(230,230,230);
                   elseif(strcmp(buf,"ARP")==0)
                                     crText= RGB(226,238,227);
                   elseif(strcmp(buf,"ICMP")==0)
                                     crText= RGB(49,164,238);
                   elseif(strcmp(buf,"HTTP")==0)
                                     crText= RGB(238,232,180);
                   elseif(strcmp(buf,"ICMPv6")==0)
                                     crText= RGB(189,254,76);
 
                   pNMCD->clrTextBk=crText;
                   *pResult= CDRF_DODEFAULT;
         }
}

         首先通過下面這段程式碼獲得新加入到List列表中的資料位置

                    POSITION pos = this->m_localDataList.FindIndex(pNMCD->nmcd.dwItemSpec);

         然後通過下面程式碼獲得新加入行中儲存的資料

                   structdatapkt * local_data = (struct datapkt *)this->m_localDataList.GetAt(pos);

         最後根據資料中對應的協議設定不同的顯示顏色。這樣,一個介面友好的列表就設定好了。 

 2.2對資料格式化顯示

          主要通過下面這個函式實現,下面這個函式主要做了兩件事:1、將資料是16進位制的形式顯示;2、將資料以字元形式顯示。程式碼相信大家都看得懂,就不做過多解釋了。

void print_packet_hex(const u_char* pkt,intsize_pkt,CString *buf)
{
         inti=0,j = 0,rowcount;
          u_char ch;
 
          char tempbuf[256];
          memset(tempbuf,0,256);
 
         for(i= 0;i<size_pkt;i+=16)
         {
                   buf->AppendFormat(_T("%04x:  "),(u_int)i);
                   rowcount= (size_pkt-i) > 16 ? 16 : (size_pkt-i);                         
 
                   for(j = 0; j < rowcount; j++)              
                            buf->AppendFormat(_T("%02x  "),(u_int)pkt[i+j]);       
 
                   //不足16,用空格補足
                   if(rowcount<16)
                            for(j=rowcount;j<16;j++)
                                               buf->AppendFormat(_T("    "));  
 
 
                   for(j = 0; j < rowcount; j++)
                   {
            ch = pkt[i+j];
            ch = isprint(ch) ? ch : '.';
                             buf->AppendFormat(_T("%c"),ch);
                   }
 
                   buf->Append(_T("\r\n"));
                   if(rowcount<16)
                            return;
         }
}

至此,如何應用MFC寫一個sniffer就完整地結束了,未講清楚的,可直接參考程式碼,這裡可進行程式碼下載