众所周知,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,好吧,是擦屁股的。。。
写完收工,运行之,如下: