wireshark源代码的一些分析

最近一段时间一直在看wireshark的源代码,没办法,项目需要嘛。这里写了一些wireshark源代码的分析,比较粗浅也比较混乱。

epan/dissector/packet-XXX.c
提供了相应协议的解析器
需要把解析器先注册到系统中,然后实现协议解析
proto_register_XXX()
dissect_XXX()


在协议解析中使用到的函数有三个变量, tvbuff_t , packet_info, proto_tree
每一个proto_item都将被添加到proto_tree中去


wiretap/
wiretap提供了大量读取文件的程序
文件读取分为两种,一种是读取正在捕获的文件,还有一种就是读取存在磁盘上的文件
每一个文件格式都有自己的文件读取的程序,如标准libpcap文件格式cap格式,就对映
libpcap.c和libpcap.h文件。
wiretap目录下的pcap_common.c和pcap_common.h提供一写文件格式的结构


标准libpcap文件是如下储存网络数据的


GLOABLE HEADER | PACKET HEADER | PACKET DATA | PACKET HEADER | PACKET DATA | 


以libpcap.c为例
GLOABLE HEADER 分为两个部分 magic number 和 pcap_hdr
程序先调用libpcap_open打开文件并判断文件类型和初始化其他变量
然后用libpcap_read读入PACKET HEADER 和 PACKET DATA。
一次调用只能读一个PACKET HEADER 和 PACKET DATA, 然后保存在wtap结构体中!所以
一个wtap结构体只能保存一个packet!

在wtap.h中保存着wtap_file_type 和 wtap_encap_type 分别表示文件类型和协议的封
装类型


/capinfos.c
这是wireshark套件中的一个工具软件,它的作用是输出文件的信息
capinfos支持所有wireshark 支持的文件格式


在capinfos中文件中帧信息主要保存在wtag结构中体。
其中process_cap_file为文件处理函数!


文件的信息被保存在capture_info结构体中
最后由print_stats函数答应输出


wtap结构体由/wiretap/file_access.c中的wtap_open_offline函数构造
函数中最重要的一个部分是确认文件的类型并调用相应的文件实例来操作文件。wireshark
支持的文件类型众多,也可以自己添加文件实例来处理自己定义的文件,具体的操作可以
在/wiretap/README.developer文件中找到。


在初始化wtap结构体的文件实例时,wtap_open_offline会在已注册的文件实例中寻找,先
调用open函数如果发现可以打开,则确定实例。(在每一个文件实例的.c文件中都有体现)
如果打不开,则调用下一个open函数!


有两个变量保存在文件类型和文件open实例,在file_access.c中。一个是wtap_open_rout
ines_t的数组,记录着所有open实例。另一个是file_type_info的结构体,记录这所有文
件类型和其他信息!


在wtap_open_offline函数的最后阶段,会开启一个for循环,在这个循环中会逐一调用文件
open的实例


当初始化完wtap结构体后,所有对文件相关的操作比如读文件中的一个帧,都会调用wtap.c
文件中的函数如wtap_read。这个函数会调用wtap结构体变量中的subtype_read函数指针来
读取文件,这个函数指针这初始化是已经被相应的文件read实例赋值。具体的赋值过程发生
在每一个文件实例的.c文件中。


在读取所有帧之后会对capture_info中的变量赋值,这样就完成了文件信息的读取。


在privileges.c在获得于平台相关的信息如用户权限等!
这个函数会在开始的时候被调用用于初始化程序。

/tshark
tshark是wireshark的文字模式,可以进行抓包和分析,这里先看分析,即无需抓包,指定文
件令其分析


main函数开始先调用init_progfile_dir找出tshark这个命令的路径,并把结果保存在/epan/
filesystem.c的变量progfile_dir中。


在捕获数据包时,需要设定比较多的参数,这些参数用capture_opts保存,这个结构体被定
义在capture_opts.h文件中。在main函数中调用capture_opts_init来初始化


/epan/timestamp.h 中定义了时间戳的相关变量和函数

在main函数中调用epan_init初始化分析器。


/epan/epan.c中定义了ethernet packet analyzer的相关操作,包括注册注销分析器等等。
/epan/dissector/register.c中注册了所有的proto_register_XXX函数和其他的相关的函数


在注册分析器的过程中,函数先初始化包所需要的内存(一个包一块emem_header_t?)


cfile中的capture_file保存了大量将被分析的文件的信息。此处文件可以是内存内的文件,
也可以是硬盘上的文件!


从1672行开始,tshark读取文件。
首先初始化capture_file这个结构体的变量cfile,调用tshark.c文件中的cf_open函数,然
后初始化时间戳。最后调用load_cap_file函数分析文件!


在load_cap_file函数在循环中调用了wtap_read函数读取一个帧,并对帧进行分析。分析的过
程是调用process_packet。在这个函数中调用了epan.c中的函数epan_dissect_run
函数,开始分析数据包。这个函数又调用了/epan/packet.c中的dissect_packet函数。


/wtap/wtap.c文件中记录着关于文件操作的相关信息,如判断文件类型,判断文件中记录数据
的包类型等等,还有就是读取文件等操作。


/epan/epan_disssect.h 中定义了一个结构体epan_dissect_t,这个结构体包含了三个结构体
变量为tvbuff, packet_info和proto_tree,这些结构体变量在分析协议时及其重要。


/epan/frame_data.h中定义的frame_data结构体变量用于保存frame_data的数据,和wth中的
frame_buffer有区别,应该是经过一定的处理,而buffer是不经处理的!


main函数在938行调用了epan_init函数,这个函数调用了packet_init函数,在packet_init中
frame_handle被初始化。在epan_init函数中也初始化了register_dissector_protocols函数,
这些函数会调用register_dissector函数,在这个函数中初始化了register_dissectors变量,
这个变量是个hash表,在初始化frame_handle时将要查找。


在packet-frame.c文件中找到的dissect_frame函数为第一个调用的dissect函数。这个函数初始
化了一个wtap_encap_dissector_table,接下来的解析将交给这个table中的dissectors.


在packet.c中,有大量关于packet的操作。其中的dissector_table的操作是用于解决各个不同
层的协议的调用问题的。在第一个调用的frame协议中,wtap_encap_dissector_table只是进行
了初始化,并没有给他添加相应的handle,即只是调用了register_dissector_table函数,并返
回了一个subdissector_table。可是frame下一层的协议,如ethernet协议就在自己注册dissector
的函数中调用了dissector_add_uint函数往frame的wtap_encap_dissector_table里添加了自己的
handle。这样frame在解析的时候就可以调用dissector_try_uint函数来调用更高层的解析器。


每一个协议都有相应的subdissetor_table来装载更高层的协议的handle。这个sub_table是保存
在packet.c文件的dissector_table这个hash变量中的。用一个名字与其相对应。wtap_encap这个
sub_table 的名字就是"wtap_encap"。每一个sub_table里有一个GList* handle变量来保存更高
层将使用的协议的handle。然后通过一个uint来找出应该调用那个handle。比如当tcp协议已经把
自己协议树中的字段解析出后就会调用dissector_try_uint函数,如果更高层是http,在80端口,
那么dissector_try_uint的两个形参就是"tcp.port"(这个一个subdissector_table)和80 。


那么这个"tcp.port"的subdissector_table在packet-tcp.c中只是生成了一个空表,并没有往这
个表里面添加什么handle用于调用http的dissector。而这个添加的过程是在packet-http.c这个文
件中实现的。这样就把协议的注册都放在了更高层的协议里了。

在每一个协议的文件中都有三个函数分别是proto_register_XXX,proto_reg_handoff_XXX,
dissect_XXX。第一个函数用于注册协议,协议的子类型和协议的各个字段。实现这三个功能
函数分别调用了proto_register_protocol,proto_register_subtree_array,
proto_register_field_array。最好函数还注册了处理这个协议所需要的dissector——调用了
register_dissector函数。


在proto_reg_handoff_XXX函数中,函数实现了向底层添加自身handle的调用——调用了
dissector_add_uint函数。


在dissect_XXX的最后解析阶段,函数调用了dissector_try_uint函数,把解析的事宜交给了更高
层的协议。


在解析协议的过程中会使用到三个结构体变量,tvbuff,packet_info,proto_tree。tvbuff变量保存
着本次解析需要用到的原始数据,也就是说每经过一次解析,这个变量的内容就会减少一部分。
packet_inofo保存这个数据包的所有信息。这个变量是随着解析的进行,一点一点被更新的。他包含
了源地址,目标地址等所有协议层的信息,方便于各个协议层之间进行交流。proto_tree是一个包含
所有协议层的结构。tree的每一个item都是一个协议层。而本协议层在经过判断之后(判断协议的类
型)后会给这个item添加一个subtree。由于proto_tree 和 proto_item是同一个struct proto_node
的两个typedef,所以item转化成subtree以后可以接续添加item。这时被添加的item就是本层协议里的
各个字段了。




在解析的过程中部分协议并不遵守既定的规则,比如一些协议占用了80端口,而http协议却从别的端
口进入计算机。在这种情况下,tcp解析协议将调用disssector_try_heur函数。这个函数和
dissector_try_uint函数一样需要一个subdissector_table,这个sub_table生成的模式和前一个一样。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
wireshark源码分析问题这几天在看wireshark(ethereal)源代码。看源代码的主要兴趣点是它的分析模块(dissect)。分析之后他的数据存在哪儿,怎么打印的(-V参数)。我想把分析后的数据,提取出来,存在自己定义的数据结构里面,或者按我自己的格式入文本中。 看了几天,对一些数据结构,似懂非懂,一些流程也是似懂非懂。可能由于经验不足的原因,搞来搞去就在几个函数,结构体里面打转。好几次以为找到切入点,发现又回来原来的起点。 这两天看晕了。有点打击,水平太差劲了。。呵呵。先这边问问,看看有没有熟悉的朋友。指点一下。先谢谢了。 这样问问题可能太细了。感觉也不大合适。 1. 我应该如何来看代码?如何找到突破点? 2. 有wireshark有了解的朋友,说说你们关于源码剖析的体会。 3. 说什么都可以,朋友觉得对我有用,有启发就好。千万别 “我顶,UP啊”。呵呵:emn23:我觉得重要的是看 pcap库 本帖最后由 peidright 于 2010-04-02 16:36 编辑 楼上说得对!。 看源代码之前,问下你自己,看代码的目的是什么? 对于 wireshark 来说,你是想学他界面? 还是抓包? 还是业务逻辑? 界面的话,wireshark 还行 抓包的话,应该看pcap库 业务逻辑的话。不应该看wireshark,看tcpdump.看下啊,:em03:看看这个也许对你有帮助 添加一个基础的RDP解析器 下面我们将循序渐进地设计一个基础的RDP解析器。它依次包含如下构成要素: 包类型字段(占用8比特位,可能的值为:1,初始;2,终结;3,数据); 标志集字段(占用8比特位:0x01,开始包;0x02,结束包;0x04先包); 序列号字段(占用16比特位); 1. 创建解析器 首先您需要选择解析器的类型:内置型(包含在主程序中)或插件型。 插件是容易编的,先做一个插件型解析器吧。 例1. 解析器初始设定. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include void proto_register_rdp(); void proto_reg_handoff_rdp(); static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree); static int proto_rdp=-1; static dissector_handle_t rdp_handle; static gint ett_rdp = -1; define TCP_PORT_RDP 3389 void proto_register_rdp(void) { proto_rdp=proto_register_protocol( "RDP Protocol", "RDP", "rdp"); } 现在来逐一分析这段代码。首先我们有一些常规的包含文件,最好依惯例在文件开始包含进来。随后是一些函数的前置声明,我们稍后定义它们。 接下来我们定义了一个整型变量"proto_rdp"用于记录我们的协议注册信息。它被初始化为"-1",当解析器注册到主程序中后,其值便会得到更新。这样做可保证我们方便地判断是否已经做了初始工作。将所有不打算对外输出的全局变量和函数声明为"static"是一个良好的习惯,因为这可以保证命名空间不被污染。通常这是容易做到的,除非您的解析器非常庞大以致跨越多个文件。 之后的模块变量"TCP_PORT_RDP"则包含了协议使用的TCP端口号,我们会对通过该端口的数据流进行解析。 solaris10下proc编译问题 >紧随其后的是解析器句柄"rdp_handle",我们稍后对它进行初始化。 至此我们已经拥有了和主程序交互的基本元素,接下来最好再把那些预声明的函数定义一下,就从注册函数"proto_register_rdp"开始吧。 首先调用函数"proto_register_protocol"注册协议。我们能够给协议起3个名字以适用不同的地方。全名和短名用在诸如"首选项(Preferences)"和"已激活协议(Enabled protocols)"对话框以及记录中已生成的域名列表内。缩略名则用于过滤器。 下面我们需要一个切换函数。 例2. 解析器切换. void proto_reg_handoff_rdp(void) { static gboolean initialized=FALSE; if(!initialized) { rdp_handle = create_dissector_handle(dissect_rdp, proto_rdp); dissector_add("tcp.port", TCP_PORT_RDP, rdp_handle); initialized=TRUE; } } 这段代码做了什么呢?如果解析器尚未初始化,则对它进行初始化。首先创建解析器。这时注册了了函数"dissect_rdp"用于完成实际的解析工作。之后将该解析器与TCP端口号相关联,以使主程序收到该端口的UDP数据流时通知该解析器。 至此我们终于可以一些解析代码了。不过目前我们仅点儿基本功能占个位置。 例3.解析 static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree) { if(check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDP"); } if(check_col(pinfo->cinfo,COL_INFO)) { col_clear(pinfo->cinfo,COL_INFO); } } 该函数用于解析传递给它的数据包。包数据由"tvb"参数指向的特殊缓冲区保管。现在我们已深入到协议的细节,对它们您肯定是了若指掌。包信息结构参数"pinfo"包含了协议的基本数据,以供我们更新。参数"tree"则指明了详细解析发生的地方。 这里我们仅做了保证通过的少量工作。前两行检查UI中"协议(Protocol)"列是否已显示。如果该列已存在,就在这儿显示我们的协议名称。这样人们就知道它被识别出来了。另外,如果"信息(INFO)"列已显示,我们就将它的内容清除。 至此我们已经准备好一个可以编译和安装的基本解析器。不过它目前只能识别和标示协议。 为了编译解析器并创建插件,还需要在解析器代码文件"packet-rdp.c"所在目录下创建一些提供支持的文件: - Makefile.am - UNIX/Linux的makefile模板 - Makefile.common - 包含了插件文件的名称 - Makefile.nmake - 包含了针对Windows平台的Wireshark插件makefile - moduleinfo.h - 包含了插件版本信息 - moduleinfo.nmake - 包含了针对Windows平台的DLL版本信息 - packet-rdp.c - 这是您的解析器原代码文件 - plugin.rc.in - 包含了针对Windows平台的DLL资源模板 "Makefile.common"和"Makefile.am"文件中涉及到相关文件和解析器名称的地方一定要修改正确。"moduldeinfo.h"和"moduleinfo.nmake"文件中的版本信息也需要正确填充。一切准备妥善后就可以将解析器编译为DLL或共享库文件了(使用nmake工具)。在wireshark文件夹下的"plugins"文件夹中,建立"rdp"文件夹。将修改过的Makefile.common,Makefile.am,moduleinfo.nmake,moduldeinfo.h,Makefile.nmake及packet-rdp.c文件考到"rdp"文件夹下,然后进行编译,rdp插件自动生成完整,就可以正常工作了。 1. 解析协议细节 现在我们已经有了一个可以运用的简单解析器,让我们再为它添点儿什么吧。首先想到的应该就是标示数据包的有效信息了。解析器在这方面给我们提供了支持。 首先要做的事情是创建一个子树以容纳我们的解析结果。这会使协议的细节显示得井井有条。现在解析器在两种情况下被调用http://www.boomss.com:其一,用于获得数据包的概要信息;其二,用于获得数据包的详细信息。这两种情况可以通过树指针参数"tree"来进行区分。如果树指针为NULL,我们只需要提供概要信息;反之,我们就需要拆解协议完成细节的显示了。基于此,让我们来增强这个解析器吧。 例4 static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree) { proto_item *ti=NULLV; if(check_col(pinfo->cinfo,COL_PROTOCOL)) { col_set_str(pinfo->cinfo,COL_PROTOCOL,"RDP"); } if(check_col(pinfo->cinfo,COL_INFO)) { col_clear(pinfo->cinfo,COL_INFO); } if(tree) { ti = proto_tree_add_item(tree, proto_rdp, tvb, offset, -1, FALSE);} } 这里我们为解析添加一个子树。它将用于保管协议的细节,仅在必要时显示这些内容。 我们还要标识被协议占据的数据区域。在我们的这种情况下,协议占据了传入数据的全部,因为我们假设协议没有封装其它内容。因此,我们用"proto_tree_add_item"函数添加新的树结点,将它添加到传入的协议树"tree"中,用协议句柄"proto_rdp"标识它,用传入的缓冲区"tvb"作为数据,并将有效数据范围的起点设为"0",长度设为"-1"(表示缓冲区内的全部数据)。至于最后的参数"FALSE",我们暂且忽略。 做了这个更改之后,在包明细面板区中应该会出现一个针对该协议的标签;选择该标签后,在包字节面板区中包的剩余内容就会高亮显示。 现在进入下一步,添加一些协议解析功能。在这一步我们需要构建一组帮助解析的表结构。这需要对"proto_register_rdp"函数做些修改。首先定义一组静态数组。 例5 定义数据结构 static hf_register_info hf[]= { { &hf;_rdp_version, { "TPKT Header:Version", "rdp.version",

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值