一步一步開發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就完整地結束了,未講清楚的,可直接參考程式碼,這裡可進行程式碼下載