众所周知,wireshark是一款强大的开源抓包与协议分析工具。wireshark是一个巨大的宝库:通过研究它的协议解析器代码,我们可以加深对协议的理解;通过调用它的导出函数,我们可以在自己的程序中添加协议解析、协议过滤等功能。
这一系列文章就将一步步介绍在Windows平台上如何调用wireshark dll中提供的导出函数。
wireshark导出函数
windows版的wireshark在安装后,在安装根目录下有一个30多MB(视版本不同有所变化)的libwireshark.dll,没错,它就是我们将要调用的dll了。使用Dependency Walker打开它,如下图所示:
红色框标出来的那个函数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,好吧,是擦屁股的。。。
写完收工,运行之,如下: