众所周知,wireshark是一款强大的开源抓包与协议分析工具。wireshark是一个巨大的宝库:通过研究它的协议解析器代码,我们可以加深对协议的理解;通过调用它的导出函数,我们可以在自己的程序中添加协议解析、协议过滤等功能。

这一系列文章就将一步步介绍在Windows平台上如何调用wireshark dll中提供的导出函数。

wireshark导出函数

windows版的wireshark在安装后,在安装根目录下有一个30多MB(视版本不同有所变化)的libwireshark.dll,没错,它就是我们将要调用的dll了。使用Dependency Walker打开它,如下图所示:

wireshark 请求状态 wireshark查看请求头_操作系统

红色框标出来的那个函数val_to_str, 我们接下来要用到。

除了从这里查看wireshark的导出函数外,还可以查看 <wireshark源码目录>\epan\libwireshark.def 这个文件,来看特定版本的wireshark导出哪些变量和函数。

环境配置与准备

OK,接下来让我们写一点代码,来调用wireshark提供给我们的val_to_str函数。

调用某个函数前,最好看看它的wireshark源码,以便我们能在自己的代码中声明此函数。比如这个val_to_str,它在<wireshark源码目录>\epan\value_string.h 中声明:



/* Tries to match val against each element in the value_string array vs.
   Returns the associated string ptr on a match.
   Formats val with fmt, and returns the resulting string, on failure. */
extern const gchar* val_to_str(const guint32 val, const value_string *vs, const char *fmt);



那么这个value_string又是什么东东呢?其实它的声明也在这个文件里:



/* Struct for the val_to_str, match_strval_idx, and match_strval functions */

typedef struct _value_string {
  guint32  value;
  const gchar   *strptr;
} value_string;



呵呵,可见val_to_str函数的作用,就是返回value_string数组vs中value为val的项的字符串strptr。如果发生错误,则用fmt格式化val并返回。

我们使用Win32 Console工程。首先必须做一些琐碎的准备工作,比如添加glib和wireshark的一些头文件和库,再比如把libwireshark.dll和它所依赖的各种dll拷贝到你的程序所在目录,随便你怎么做,总之,要让你的程序找得到libwireshark.dll和它的依赖项。比如我设置了:

C/C++ - 常规 - 附加包含目录:
"E:\dev\WiresharkDev1.8.4\gtk+-bundle_2.24.10-2.7_win32ws\include\glib-2.0";
"E:\dev\WiresharkDev1.8.4\gtk+-bundle_2.24.10-2.7_win32ws\lib\glib-2.0\include";
"E:\dev\wireshark-1.8.4"

链接器 - 常规 - 附加库目录
"E:\dev\WiresharkDev1.8.4\gtk+-bundle_2.24.10-2.7_win32ws\lib"

链接器 - 输入 - 附加依赖项
glib-2.0.lib

然后,我把wireshark安装目录下,几乎所有dll拷贝到了我的工程的debug或release目录。

开发代码

其实代码很简单,主要过程就是:

加载libwireshark.dll -> 从DLL实例中获取函数val_to_str的地址 -> 调用val_to_str。

代码如下:



1 /*
 2  * 调用wireshark的一个小函数
 3  *
 4  * Copyright (c) 2013 赵子清, All rights reserved.
 5  *
 6  */
 7 
 8 
 9 #include <Windows.h>
10 #include <stdio.h>
11 #include "epan/value_string.h"
12 
13 
14 const value_string vs[] =  
15 {  
16     {1, "C++"},  
17     {2, "java"},  
18     {3, "php"}  
19 };  
20 
21 typedef void (*f_emem_init) (void);
22 typedef void (*f_ep_free_all) (void);  
23 typedef const gchar* (*f_val_to_str) (guint32 val, const value_string *vs, const char *fmt);  
24 
25 
26 int main(int argc, char* argv[])  
27 {  
28     HINSTANCE  hDLL;  
29     f_emem_init     ws_emem_init;
30     f_ep_free_all   ws_ep_free_all;  
31     f_val_to_str    ws_val_to_str;  
32 
33     hDLL = ::LoadLibrary("libwireshark.dll");  
34     if(!hDLL)  
35     {  
36         printf("Can't load DLL!\n");  
37     }  
38     else  
39     {  
40         ws_emem_init = (f_emem_init)::GetProcAddress(hDLL, "emem_init");
41         ws_ep_free_all = (f_ep_free_all)::GetProcAddress(hDLL, "ep_free_all");
42         ws_val_to_str = (f_val_to_str)::GetProcAddress(hDLL, "val_to_str");  
43         if(!hDLL)  
44         {  
45             ::FreeLibrary(hDLL);  
46             printf("Can't get function!\n");  
47         }  
48         else  
49         {  
50             const gchar* ret;  
51             const gchar* fmt = "item not exist: %d";
52             ws_emem_init();
53             ret = ws_val_to_str(2, vs, fmt); 
54             printf("%s\n", ret);
55             ret = ws_val_to_str(9, vs, fmt); 
56             printf("%s\n", ret);
57             ret = ws_val_to_str(2, NULL, fmt);
58             printf("%s\n", ret);
59             ws_ep_free_all();
60         }  
61     }  
62 
63     system("PAUSE");  
64     return 0;  
65 }



有同学可能要问了:不是说好调用val_to_str的吗?为什么还要调用emem_init和ep_free_all?

是这样的,前面提到过,当val_to_str函数各种失败时,它就用fmt格式化val,并返回。而返回的是一个字符串指针,那么它的内存需要由wireshark内部来进行分配,所以,就用到emem_init了,它在wireshark启动时调用一次,初始化内部的内存分配机制。而ep_free_all,好吧,是擦屁股的。。。

写完收工,运行之,如下:

wireshark 请求状态 wireshark查看请求头_导出函数_02