說起來有一些慚愧,研究wireshark有一段時間了,但是對源代碼的分析卻至今沒有什么進展。。。
最初想要研究wireshark是因為我的開題是基於wireshark來做的。
現在有很多抓包工具,wireshark的優勢在於完全開源,分析功能強大,但其缺點也很明顯,即捕獲的數據包存儲過於分散,大數據背景下,不能有效的對海量的數據包進行存儲分析,因此將wireshark捕獲到的數據存儲到專門的數據庫中是非常必要的。(當然,存儲數據只是第一步,接下來還要進行對數據的分析工作)
真正開始分析wireshark了,首先是編譯。unbutu下很順利就編譯通過了,但是在windows下卻出現各種問題,至今沒有編譯通過。。。,分析wireshark源代碼,在windows下編譯通過有必要嗎?
后來看關於wireshark的論文,接觸到了兩個東西。即winpcap、libpcap。
winpcap是底層的,在windows上wireshark是依賴於winpcap來截包的,在linux上則是依賴於libpcap。而winpcap又是基於libpcap的設計基礎上開發設計的,使用方法也和libpcap基本相同。
那么,我在分析wireshark存儲部分源代碼的時候有必要看winpcap和libpcap的函數庫嗎?
到真正看wireshark源代碼了,200多M,算上子文件共2000多個,對於一個不是計算機科班出身的我來說,不得不說,有點困難。。 從百度文庫上下載了些資料,主要是分析wireshark邏輯功能模塊,最有價值的應該是這篇了,如下:
a) GTK1/2
處理用戶的輸入輸出顯示,源碼在gtk目錄.
b) Core
核心模塊,通過函數調用將其他模塊連接在一起,源碼在根目錄
c) Epan
wireshark Packetage Analyzing,包分析引擎,源碼在epan目錄
Protocol-Tree:保存數據包的協議信息,wireshark的協議結構采用樹形結構,解析協議報文時只需要從根節點通過函數句柄依次調用各層解析函數即可。
Dissectors:在epan/dissector目錄下,各種協議解碼器,支持700+種協議解析,對於每種協議,解碼器都能識別出協議字段(field),並顯示出字段值(field value)由於網絡協議種類很多,為了使協議和協議間層次關系明顯,對數據流里的各個層次的協議能夠逐層處理,wireshark系統采用了協議樹的方式。
Plugins:一些協議解碼器以插件形式實現,源碼在plugins目錄
Display-Filters:顯示過濾引擎,源碼在epan/dfilter目錄
d) Capture
捕包引擎,利用libpcap/WinPcap從底層抓取網絡數據包,libpcap/WinPcap提供了通用的抓包接口,能從不同類型的網絡接口(包括以太網,令牌環網,ATM網等)獲取數據包。
e) Wiretap
從文件中讀取數據包,支持多種文件格式,源碼在wiretap目錄
f) Win-/libpcap
Wireshark抓包時依賴的庫文件
wireshark功能模塊
3. wireshark流程分析
1) 初始化
Wireshark的初始化包括一些全局變量的初始化、協議分析引擎的初始化和Gtk相關初始化,顯示Ethereal主窗口,等待用戶進一步操作。重點就是Epan模塊的初始化。
Epan初始化:
tvbuff初始化:全局變量tvbuff_mem_chunk指向用memchunk分配的固定大小的空閑內存塊,每個內存塊是tvbuff_t結構,從空閑內存塊中取出后,用來保存原始數據包。
協議初始化:
全局變量:
proto_names
proto_short_names
proto_filter_names
以上三個全局變量主要用來判斷新注冊的協議名是否重復,如果重復,給出提示信息,在協議解析過程中並沒有使用。
協議注冊:
注冊協議:將三個參數分別注冊給proto_names、proto_short_names、proto_filter_names三個全局變量中,
注冊字段,需要在wireshark協議樹顯示的報文內容字段。
協議解析表
Handoff注冊
將協議與父協議節點關聯起來
Packet(包)初始化
全局變量:
frame_handle:協議解析從frame開始,層層解析,直到所有的協議都解析完為止。frame_handle保存了frame協議的handle。
data_handle:有的協議無法從frame開始,那么就從data開始。原理同frame。
讀配置文件preference
讀capture filter和display filter文件,分別保存在全局變量capture_filter和display_filter中。
讀disabled protocols文件,保存全局變量global_disabled_protos和disabled_protos中
初始化全局變量cfile
Cfile是個重要的變量,數據類型為capture file,它保存了數據包的所有信息,
取得命令行啟動時,參數列表,並進行相應的處理
2) 處理流程
Wireshark初始化完成以后進入實際處理階段,主程序創建抓包進程,捕包進程和主程序是通過PIPE進行傳遞數據的,主程序把抓取的數據寫入臨時文件,通過函數add_packet_to_packet_list將數據包加入包列表。處理時,主程序從列表中選取一個數據包,提取該數據包中的數據填寫在數據結構中,最后調用協議解析函數epan_dissect_run進行處理,從epan_dissect_run開始,是實際的協議解析過程,
下面以HTTP協議報文為例,流程如下:
a) 解析frame層
調用函數dissect_frame對frame層進行解析,並在協議樹上填充相應字段信息。函數最后會判斷是否有上層協議封裝,如果有則調用函數dissector_try_port在協議樹上查找對應的解析函數,這里函數dissector_try_port根據pinfo->fd->lnk_t查找對應的上層協議處理函數,pinfo->fd->lnk_t值為1,上層封裝協議為以太網協議,全局結構體指針變量dissector_handle當前的協議解析引擎句柄置為dissect_eth_maybefcs,至此,frame層解析結束。
b) 解析以太網層
函數call_dissector_work根據dissector_handle調用frame上層協議解析函數dissect_eth_maybefcs對以太網層進行解析,並在協議樹上填充相應字段,包括目的MAC地址和以太網上層協議類型等信息。函數最后會判斷是否有上層協議封裝,如果有則調用函數dissector_try_port在協議樹上查找對應的解析函數,這里函數dissector_try_port根據etype查找對應的上層協議處理函數,以太網字段etype為0800的報文是ip報文,上層封裝協議為IP協議,全局結構體指針變量dissector_handle當前的協議解析引擎句柄置為dissect_ip,至此,以太網層解析結束。
c) 解析IP層
函數call_dissector_work根據dissector_handle調用以太網上層協議解析函數dissect_ip對以太網層進行解析,並在協議樹上填充相應字段,包括版本號,源地址,目的地址等信息。函數最后會判斷是否有上層協議封裝,如果有則調用函數dissector_try_port在協議樹上查找對應的解析函數,這里函數dissector_try_port根據nxt (nxt = iph->ip_p)查找對應的上層協議處理函數,以太網字段nxt為06的報文是TCP報文,上層封裝協議為TCP協議,全局結構體指針變量dissector_handle當前的協議解析引擎句柄置為dissect_tcp,至此,IP層解析結束。
d) 解析TCP層
函數call_dissector_work根據dissector_handle調用以太網上層協議解析函數dissect_tcp對TCP層進行解析,包括對TCP頭的解析和選項字段的解析,並在協議樹上填充相應字段,包括源端口,目的端口,標志位等信息。函數最后會判斷是否有上層協議封裝,如果有則調用函數dissector_try_port在協議樹上查找對應的解析函數,這里函數dissector_try_port根據port查找對應的上層協議處理函數,將源端口和目的端口分別賦值給low_port和high_port,根據low_port和high_port分別匹配上層協議解析函數,port為80的報文是HTTP報文,上層封裝協議為HTTP協議,全局結構體指針變量dissector_handle當前的協議解析引擎句柄置為dissect_http,至此,TCP層解析結束。
e) 解析HTTP層
至此wireshark進入應用層協議檢測階段,wireshark解析dissect_http函數中注冊的字段,並提取相應的字段值添加到協議樹中,應用層的具體解析流程將在下面介紹。HTTP協議具體函數調用過程參見:
重要的數據結構
struct _epan_dissect_t {
tvbuff_t *tvb;//用來保存原始數據包
proto_tree *tree;//協議樹結構
packet_info pi;// 包括各種關於數據包和協議顯示的相關信息
};
/** Each proto_tree, proto_item is one of these. */
typedef struct _proto_node {
struct _proto_node *first_child;//協議樹節點的第一個子節點指針
struct _proto_node *last_child; //協議樹節點的最后一個子節點指針
struct _proto_node *next; //協議樹節點的下一個節點指針
struct _proto_node *parent;//父節點指針
field_info *finfo;//保存當前協議要顯示的地段
tree_data_t *tree_data;//協議樹信息
} proto_node;
typedef struct _packet_info {
const char *current_proto; //當前正在解析的協議名稱
column_info *cinfo; //wireshark顯示的信息
frame_data *fd;//現在分析的原始數據指針
union wtap_pseudo_header *pseudo_header;//frame類型信息
GSList *data_src; /*frame層信息 */
address dl_src; /* 源MAC */
address dl_dst; /*目的MAC */
address net_src; /* 源IP */
address net_dst; /*目的IP */
address src; /*源IP */
address dst; /*目的IP */
guint32 ethertype; /*以太網類型字段*/
guint32 ipproto; /* IP協議類型*/
guint32 ipxptype; /* IPX 包類型 */
guint32 mpls_label; /* MPLS包標簽*/
circuit_type ctype;
guint32 circuit_id; /*環路ID */
const char *noreassembly_reason; /* 重組失敗原因*/
gboolean fragmented; /*為真表示未分片*/
gboolean in_error_pkt; /*錯誤包標志*/
port_type ptype; /*端口類型 */
guint32 srcport; /*源端口*/
guint32 destport; /*目的端口*/
guint32 match_port; /*進行解析函數匹配時的匹配端口*/
const char *match_string; /*調用子解析引擎時匹配的協議字段指針*/
guint16 can_desegment; /* 能否分段標志*/
guint16 saved_can_desegment;
int desegment_offset; /*分段大小*/
#define DESEGMENT_ONE_MORE_SEGMENT 0x0fffffff
#define DESEGMENT_UNTIL_FIN 0x0ffffffe
guint32 desegment_len;
guint16 want_pdu_tracking;
guint32 bytes_until_next_pdu;
int iplen; /*IP包總長*/
int iphdrlen; /*IP頭長度*/
int p2p_dir;
guint16 oxid; /* next 2 fields reqd to identify fibre */
guint16 rxid; /* channel conversations */
guint8 r_ctl; /* R_CTL field in Fibre Channel Protocol */
guint8 sof_eof;
guint16 src_idx; /* Source port index (Cisco MDS-specific) */
guint16 dst_idx; /* Dest port index (Cisco MDS-specific) */
guint16 vsan; /* Fibre channel/Cisco MDS-specific */
/* Extra data for DCERPC handling and tracking of context ids */
guint16 dcectxid; /* Context ID (DCERPC-specific) */
int dcetransporttype;
guint16 dcetransportsalt; /* fid: if transporttype==DCE_CN_TRANSPORT_SMBPIPE */
#define DECRYPT_GSSAPI_NORMAL 1
#define DECRYPT_GSSAPI_DCE 2
guint16 decrypt_gssapi_tvb;
tvbuff_t *gssapi_wrap_tvb;
tvbuff_t *gssapi_encrypted_tvb;
tvbuff_t *gssapi_decrypted_tvb;
gboolean gssapi_data_encrypted;
guint32 ppid; /* SCTP PPI of current DATA chunk */
guint32 ppids[MAX_NUMBER_OF_PPIDS]; /* The first NUMBER_OF_PPIDS PPIDS which are present * in the SCTP packet*/
void *private_data; /* pointer to data passed from one dissector to another */
/* TODO: Use emem_strbuf_t instead */
GString *layer_names; /* layers of each protocol */
guint16 link_number;
guint8 annex_a_used;
guint16 profinet_type; /* the type of PROFINET packet (0: not a PROFINET packet) */
void *profinet_conv; /* the PROFINET conversation data (NULL: not a PROFINET packet) */
void *usb_conv_info;
void *tcp_tree; /* proto_tree for the tcp layer */
const char *dcerpc_procedure_name; /* Used by PIDL to store the name of the current dcerpc procedure */
struct _sccp_msg_info_t* sccp_info;
guint16 clnp_srcref; /* clnp/cotp source reference (can't use srcport, this would confuse tpkt) */
guint16 clnp_dstref; /* clnp/cotp destination reference (can't use dstport, this would confuse tpkt) */
guint16 zbee_cluster_id; /* ZigBee cluster ID, an application-specific message identifier that
* happens to be included in the transport (APS) layer header.
*/
guint8 zbee_stack_vers; int link_dir; /* 3GPP messages are sometime different UP link(UL) or Downlink(DL)*/
} packet_info;
因為我的主要工作是修改wireshark存儲部分源代碼,還有必要從main開始分析嗎?看到一層層的調用,要調暈了。。。。
博客寫的有點亂了,總之,不知道如何開始。。。。