上文【調用wireshark(一):初次嘗試 http://www.cnblogs.com/zzqcn/archive/2013/05/11/3072362.html 】已經介紹了調用wireshark的原理,並給出一個簡單示例。本文要給出真正調用wireshark協議解析函數的方法和代碼。
本文的討論和代碼基於wireshark 1.8.4版本。
涉及到的函數與數據結構
首先我們得知道需要調用哪些函數。通過調試自己編譯的wireshark,我發現如果要實現簡單的協議解析,主要只需要以下幾個函數(源碼位置均相對於wireshark源碼主目錄而言):
函 數 | 功 能 | 源碼位置 |
epan_init | 初始化協議解析庫 | epan/epan.h |
epan_cleanup | 清理協議解析庫 | 同上 |
epan_dissect_new | 創建協議解析數據結構edt | 同上 |
epan_dissect_run | 執行協議解析 | 同上 |
epan_dissect_free | 銷毀協議解析數據結構edt | 同上 |
init_dissection | 初始化數據包級協議解析 | epan/packet.h |
cleanup_dissection | 清理數據包級協議解析 | 同上 |
除此之外,還需要導出一些輔助的函數,如register_all_protocols, register_all_protocol_handoffs,proto_item_fill_label等等。
不僅如此,還需要熟悉協議解析過程所涉及到的一些數據結構,主要有:
數據結構 | 功 能 | 源碼位置 |
epan_dissect_t | 協議解析信息,保存協議數據及協議解析樹 | epan/epan.h; epan/epan_dissect.h |
field_info | 協議字段信息 | epan/proto.h |
header_field_info | 協議首部字段信息 | 同上 |
proto_tree/proto_node | 協議樹 | 同上 |
frame_data | 單幀(數據包)信息 | epan/frame_data.h |
wtap_pseudo_header | wtap偽首部,主要是鏈路層協議信息 | wiretap/wtap.h |
以上就是一些主要的函數及數據結構。實際的協議解析過程中,可能會涉及到更多的函數及數據結構,這里就不多說了,具體可以查看wireshark源碼。如果對於某些函數,或者解析過程有不了解的,也可以自己編譯wireshark,然后調試它。
代碼實現
知道原理及所需的函數后,就可以編碼實現了。環境的配置等基礎知識,本系列前一篇已經講過了。
我繼續用Win32 Console工程來寫這個示例。我這個示例代碼分為兩個部分:
一、wireshark導出函數及簡單的封裝;
二、實際解析代碼
第一個部分分成wireshark.h和wireshark.cpp兩個文件。第二部分為dissector.cpp,其中也包括了main函數。
wireshark導出函數及簡單封裝
沒什么好說的,主要就是所需函數的聲明,以及動態調用代碼。
wireshark.h:
1 /* 2 * wireshark協議解析相關的導出函數聲明,以及簡單函數封裝 3 * 4 * Copyright (c) 2013 趙子清, All rights reserved. 5 * 6 */ 7 8 9 #ifndef __WIRESHARK_H__ 10 #define __WIRESHARK_H__ 11 12 // see \wireshark-1.8.4\CMakeLists.txt, #481 13 #define WS_VAR_IMPORT __declspec(dllimport) extern 14 // see \wireshark-1.8.4\CMakeLists.txt, #482 15 #define WS_MSVC_NORETURN __declspec(noreturn) 16 17 #ifdef TRY 18 #undef TRY 19 #endif 20 #ifdef CATCH 21 #undef CATCH 22 #endif 23 #ifdef CATCH_ALL 24 #undef CATCH_ALL 25 #endif 26 #ifdef THROW 27 #undef THROW 28 #endif 29 30 31 // wireshark源碼頭文件 32 #include "epan/epan.h" 33 #include "epan/epan_dissect.h" 34 #include "epan/proto.h" 35 #include "epan/packet_info.h" 36 #include "epan/frame_data.h" 37 #include "epan/packet.h" 38 #include <Windows.h> 39 40 41 #define CHECK(x) if(!(x)) return FALSE; 42 43 44 /* \register.h -------------------------------------------------------------------------*/ 45 typedef void (*register_cb) (register_action_e action, const char *message, gpointer client_data); 46 typedef void (*f_register_all_protocols) (register_cb cb, gpointer client_data); 47 typedef void (*f_register_all_protocol_handoffs) (register_cb cb, gpointer client_data); 48 typedef void (*f_register_all_tap_listeners)(void); 49 /*--------------------------------------------------------------------------------------*/ 50 51 /* \epan\packet.h ----------------------------------------------------------------------*/ 52 typedef void (*f_init_dissection) (void); 53 typedef void (*f_cleanup_dissection) (void); 54 /*--------------------------------------------------------------------------------------*/ 55 56 /* \epan\epan.h -------------------------------------------------------------------------*/ 57 typedef void (*f_epan_init) (void (*register_all_protocols)(register_cb cb, gpointer client_data), 58 void (*register_all_handoffs)(register_cb cb, gpointer client_data), 59 register_cb cb, 60 void *client_data, 61 void (*report_failure)(const char *, va_list), 62 void (*report_open_failure)(const char *, int, gboolean), 63 void (*report_read_failure)(const char *, int)); 64 typedef void (*f_epan_cleanup) (void); 65 typedef epan_dissect_t* (*f_epan_dissect_new) (gboolean create_proto_tree, 66 gboolean proto_tree_visible); 67 typedef void (*f_epan_dissect_run) (epan_dissect_t *edt, void* pseudo_header, 68 const guint8* data, frame_data *fd, column_info *cinfo); 69 typedef void (*f_epan_dissect_free) (epan_dissect_t* edt); 70 typedef void (*f_epan_dissect_fill_in_columns) (epan_dissect_t *edt); 71 /*--------------------------------------------------------------------------------------*/ 72 73 /* \epan\proto.h -----------------------------------------------------------------------*/ 74 typedef void (*f_proto_item_fill_label) (field_info *fi, gchar *label_str); 75 /*--------------------------------------------------------------------------------------*/ 76 77 extern f_epan_init ws_epan_init; 78 extern f_epan_cleanup ws_epan_cleanup; 79 extern f_register_all_protocols ws_register_all_protocols; 80 extern f_register_all_protocol_handoffs ws_register_all_protocol_handoffs; 81 extern f_init_dissection ws_init_dissection; 82 extern f_cleanup_dissection ws_cleanup_dissection; 83 extern f_epan_dissect_new ws_epan_dissect_new; 84 extern f_epan_dissect_run ws_epan_dissect_run; 85 extern f_epan_dissect_free ws_epan_dissect_free; 86 extern f_proto_item_fill_label ws_proto_item_fill_label; 87 88 89 HINSTANCE LoadWiresharkDLL(const TCHAR* szDLLPath); 90 BOOL FreeWiresharkDLL(HMODULE hModule); 91 BOOL GetWiresharkFunctions(HMODULE hDLL); 92 93 #endif /* WIRESHARK_H_ */
wireshark.cpp:
1 /* 2 * wireshark協議解析相關的導出函數聲明,以及簡單的函數封裝 3 * 4 * Copyright (c) 2013 趙子清, All rights reserved. 5 * 6 */ 7 8 9 #include "wireshark.h" 10 11 f_epan_init ws_epan_init; 12 f_epan_cleanup ws_epan_cleanup; 13 f_register_all_protocols ws_register_all_protocols; 14 f_register_all_protocol_handoffs ws_register_all_protocol_handoffs; 15 f_init_dissection ws_init_dissection; 16 f_cleanup_dissection ws_cleanup_dissection; 17 f_epan_dissect_new ws_epan_dissect_new; 18 f_epan_dissect_run ws_epan_dissect_run; 19 f_epan_dissect_free ws_epan_dissect_free; 20 f_proto_item_fill_label ws_proto_item_fill_label; 21 22 HINSTANCE LoadWiresharkDLL(const TCHAR* szDLLPath) 23 { 24 return ::LoadLibrary(szDLLPath); 25 } 26 27 BOOL FreeWiresharkDLL(HMODULE hModule) 28 { 29 return ::FreeLibrary(hModule); 30 } 31 32 BOOL GetWiresharkFunctions(HMODULE hDLL) 33 { 34 CHECK(ws_epan_init = (f_epan_init)::GetProcAddress(hDLL, "epan_init")); 35 CHECK(ws_epan_cleanup = (f_epan_cleanup)::GetProcAddress(hDLL, "epan_cleanup")); 36 CHECK(ws_register_all_protocols = (f_register_all_protocols) 37 ::GetProcAddress(hDLL, "register_all_protocols")); 38 CHECK(ws_register_all_protocol_handoffs = (f_register_all_protocol_handoffs) 39 ::GetProcAddress(hDLL, "register_all_protocol_handoffs")); 40 CHECK(ws_init_dissection = (f_init_dissection)::GetProcAddress(hDLL, "init_dissection")); 41 CHECK(ws_cleanup_dissection = (f_cleanup_dissection)::GetProcAddress(hDLL, "cleanup_dissection")); 42 CHECK(ws_epan_dissect_new = (f_epan_dissect_new)::GetProcAddress(hDLL, "epan_dissect_new")); 43 CHECK(ws_epan_dissect_run = (f_epan_dissect_run)::GetProcAddress(hDLL, "epan_dissect_run")); 44 CHECK(ws_epan_dissect_free = (f_epan_dissect_free)::GetProcAddress(hDLL, "epan_dissect_free")); 45 CHECK(ws_proto_item_fill_label = (f_proto_item_fill_label)::GetProcAddress(hDLL, "proto_item_fill_label")); 46 47 return TRUE; 48 }
實際解析代碼
以下代碼調用wireshark協議解析庫,解析了一段數據。這段數據,如注釋里所說,是我上網時隨便用wireshark抓的。解析完成后,把結果輸出到控制台。
主要的流程是:
動態調用所需的wireshark函數 -> 初始化協議解析庫 -> 解析數據 -> 將解析結果按協議層次輸出到控制台 -> 清理協議解析庫。
解析的結果主要是一個樹形結構,因為我寫了一個遞歸函數print_tree來遍歷此樹。
1 /* 2 * 調用wireshark解析庫完成數據解析 3 * 4 * Copyright (c) 2013 趙子清, All rights reserved. 5 * 6 */ 7 8 #include "wireshark.h" 9 #include <stdio.h> 10 #include <tchar.h> 11 12 #define DATA_LEN 73 13 #define WIRESHARK_DLL_PATH _T("E:\\dev\\wireshark-1.8.4\\release\\libwireshark.dll") 14 15 // 幀數據, 不包括PCAP文件頭和幀頭 16 // 數據為ethernet - ipv4 - udp - DNS, 上網時隨便捕獲的. 17 const guchar data[DATA_LEN] = 18 { 19 0x7E, 0x6D, 0x20, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 20 0x00, 0x3B, 0x5F, 0x15, 0x00, 0x00, 0x40, 0x11, 0xF1, 0x51, 0x73, 0xAB, 0x4F, 0x08, 0xDB, 0x8D, 21 0x8C, 0x0A, 0x9B, 0x90, 0x00, 0x35, 0x00, 0x27, 0xEF, 0x4D, 0x43, 0x07, 0x01, 0x00, 0x00, 0x01, 22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x04, 0x73, 0x69, 0x6E, 0x61, 0x03, 0x63, 0x6F, 23 0x6D, 0x02, 0x63, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x01 24 }; 25 26 27 void print_tree(proto_tree* tree, int level) 28 { 29 if(tree == NULL) 30 return; 31 32 for(int i=0; i<level; ++i) 33 printf(" "); 34 35 gchar field_str[ITEM_LABEL_LENGTH + 1] = {0}; 36 if(tree->finfo->rep == NULL) 37 ws_proto_item_fill_label(tree->finfo, field_str); 38 else 39 strcpy_s(field_str, tree->finfo->rep->representation); 40 41 if(!PROTO_ITEM_IS_HIDDEN(tree)) 42 printf("%s\n", field_str); 43 44 print_tree(tree->first_child, level+1); 45 print_tree(tree->next, level); 46 } 47 48 void try_dissect() 49 { 50 frame_data *fdata; 51 epan_dissect_t *edt; 52 union wtap_pseudo_header pseudo_header; 53 pseudo_header.eth.fcs_len = -1; 54 55 fdata = (frame_data*)g_new(frame_data, 1); 56 57 memset(fdata, 0, sizeof(frame_data)); 58 fdata->pfd = NULL; 59 fdata->num = 1; 60 fdata->interface_id = 0; 61 fdata->pkt_len = DATA_LEN; 62 fdata->cap_len = DATA_LEN; 63 fdata->cum_bytes = 0; 64 fdata->file_off = 0; 65 fdata->subnum = 0; 66 fdata->lnk_t = WTAP_ENCAP_ETHERNET; 67 fdata->flags.encoding = PACKET_CHAR_ENC_CHAR_ASCII; 68 fdata->flags.visited = 0; 69 fdata->flags.marked = 0; 70 fdata->flags.ref_time = 0; 71 fdata->color_filter = NULL; 72 fdata->abs_ts.secs = 0; 73 fdata->abs_ts.nsecs = 0; 74 fdata->opt_comment = NULL; 75 76 edt = ws_epan_dissect_new(TRUE, TRUE); 77 ws_epan_dissect_run(edt, &pseudo_header, data, fdata, NULL); 78 print_tree(edt->tree->first_child, 0); 79 ws_epan_dissect_free(edt); 80 g_free(fdata); 81 } 82 83 84 int main(int argc, char** argv) 85 { 86 HINSTANCE hDLL = NULL; 87 BOOL ret = FALSE; 88 void* addr = NULL; 89 90 hDLL = LoadWiresharkDLL(WIRESHARK_DLL_PATH); 91 if(hDLL) 92 { 93 ret = GetWiresharkFunctions(hDLL); 94 if(ret) 95 { 96 ws_epan_init(ws_register_all_protocols, ws_register_all_protocol_handoffs, 97 NULL, NULL, NULL, NULL, NULL); 98 ws_init_dissection(); 99 try_dissect(); 100 ws_cleanup_dissection(); 101 ws_epan_cleanup(); 102 } 103 else 104 fprintf(stderr, "某些導出函數獲取失敗!\n"); 105 FreeWiresharkDLL(hDLL); 106 } 107 else 108 fprintf(stderr, "無法加載DLL!\n"); 109 110 111 system("PAUSE"); 112 return 0; 113 }
解析結果
編譯運行以上代碼,控制台輸出的解析結果如下:
Frame 1: 73 bytes on wire (584 bits), 73 bytes captured (584 bits) WTAP_ENCAP: 1 Frame Number: 1 Frame Length: 73 bytes (584 bits) Capture Length: 73 bytes (584 bits) Frame is marked: False Frame is ignored: False Protocols in frame: eth:ip:udp:dns Ethernet II, Src: 01:00:01:00:00:00 (01:00:01:00:00:00), Dst: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00) Destination: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00) Address: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00) .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default) .... ...0 .... .... .... .... = IG bit: Individual address (unicast) Source: 01:00:01:00:00:00 (01:00:01:00:00:00) Expert Info (Warn/Protocol): Source MAC must not be a group address: IEEE 802.3-2002, Section 3.2.3(b) Message: Source MAC must not be a group address: IEEE 802.3-2002, Section 3.2.3(b) Severity level: Warn Group: Protocol Address: 01:00:01:00:00:00 (01:00:01:00:00:00) .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default) .... ...1 .... .... .... .... = IG bit: Group address (multicast/broadcast) Type: IP (0x0800) Internet Protocol Version 4, Src: 115.171.79.8 (115.171.79.8), Dst: 219.141.140.10 (219.141.140.10) Version: 4 Header length: 20 bytes Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) 0000 00.. = Differentiated Services Codepoint: Default (0x00) .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00) Total Length: 59 Identification: 0x5f15 (24341) Flags: 0x00 0... .... = Reserved bit: Not set .0.. .... = Don't fragment: Not set ..0. .... = More fragments: Not set Fragment offset: 0 Time to live: 64 Protocol: UDP (17) Header checksum: 0xf151 [correct] Good: True Bad: False Source: 115.171.79.8 (115.171.79.8) Destination: 219.141.140.10 (219.141.140.10) Source GeoIP: Unknown Destination GeoIP: Unknown User Datagram Protocol, Src Port: 39824 (39824), Dst Port: 53 (53) Source port: 39824 (39824) Destination port: 53 (53) Length: 39 Checksum: 0xef4d [validation disabled] Good Checksum: False Bad Checksum: False Domain Name System (query) Transaction ID: 0x4307 Flags: 0x0100 Standard query 0... .... .... .... = Response: Message is a query .000 0... .... .... = Opcode: Standard query (0) .... ..0. .... .... = Truncated: Message is not truncated .... ...1 .... .... = Recursion desired: Do query recursively .... .... .0.. .... = Z: reserved (0) .... .... ...0 .... = Non-authenticated data: Unacceptable Questions: 1 Answer RRs: 0 Authority RRs: 0 Additional RRs: 0 Queries t.sina.com.cn: type A, class IN Name: t.sina.com.cn Type: A (Host address) Class: IN (0x0001)
我們當然也可以在自己的GUI界面上,使用TreeCtrl來把解析結果顯示給用戶,就像下面這樣(我寫的工具的截圖):