调用wireshark(二):调用协议解析器
上文【调用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来把解析结果显示给用户,就像下面这样(我写的工具的截图):