wireshark源码探索No.1---编译,调试,阅读

每一个做过网络安全的工程师,都对wireshark有很深的感情,市面上也充斥各种wireshark的培训手册,手把手练习博文,但是真正面向开发者,面向developer的博文却少的可怜,CSDN仅有的几篇博文,内容虽然也很有价值,但是是站在一个程序的角度去剖析代码,内容并非每个开发者想要的内容。
最近一直在看这方面的资料,也有一些自己的心得,所以想从一个开发者的角度,去了解wireshark,将wireshark的经典内容介绍出来,以给更多的人去了解这个利器。


我的学习习惯是要想了解一份开源软件,首先down下代码,编译通过,熟悉使用方法,再去从代码角度了解会有事半功倍的效果。否则直接拿出代码来啃,可能会将自己陷入到一个个的坑中。所以wireshark的了解,也从编译开始:

1、编译

(编译环境建议大家还是在linux里面更容易,毕竟网络安全开发基本都是在Linux里面搞的)

  1. down下源代码,这不不比多说,https://www.wireshark.org/
  2. 解压源码,tar jxvf wireshark-2.2.6.tar.bz2
  3. 瞅瞅INSTALL文件,咋编译,./configure;make;make install就这么简单?

最好./configure -h看看大概的编译选项,比如看到--enable-wireshark build the Wireshark GUI (with Gtk+, Qt, or both),说明是可以开启wireshark编译的,但是wireshark的GUI版本我们并不需要(自己安装一个就好了,非要去编译干嘛),那不编译wireshark我们怎么调试呢?
tshark!了解代码用它就足够了
所以编译选项直接./configure --disable-wireshark即可,中间可能会有检查一些内容不通过,一般都是缺少开发库啥的,自行安装即可。最后大概如下:
这里写图片描述

然后make,漫长的编译过程结束后(要多长有多长,你的有多长,可以留言下让我嫉妒嫉妒),就可以在本地看到一个tshark程序了,这个时候一个就来了。

2、调试坑

当你使用gdb tshark调试的时候,会出现不能调试的问题,google一下,可能你会找到https://unix.stackexchange.com/questions/86342/tshark-not-running-via-gdb这里的介绍,说加个libtool –mode=execute gdb ./tshark就好了,但是真正调试的时候会有各种奇怪的问题。其实这就是上面说的那个

这里tshark仅仅是一个shell脚本,他会调用真正的tshark程序,那真正的程序在哪里呢?find一下,就在.libs下面,但是你调试它的时候又显示各种so库不存在。所以最好编译完以后make install一下,让各个so库归位,便于你调试,只要ldd tshark显示各个库都能找到就行了。

因为环境不尽相同,可能你即使make install也没有找到,可能是环境变量没有生效,ldconfig一下就差不多了。

3、再调试

调试我们以ftp协议为例,(ftp协议代码比较简单,且包含内容比较多,后续都以他为例)

gdb .libs/tshark
b dissect_ftp
r -r ftp.cap
bt

bt后就会显示出所有的调用关系来:

ftp调用关系图

#0  dissect_ftp (tvb=0xa32ed0, pinfo=0xb42418, tree=0x0, data=0x7fffffffcd50) at packet-ftp.c:516

#1  0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe5ea0a40, handle=0x7fffe5ea0a40, data=0x7fffffffcd50, tree=0x0, pinfo=0xb42418, tvb=0xa32ed0) at packet.c:648
#2  call_dissector_work (handle=0x7fffe5ea0a40, tvb=0xa32ed0, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0x7fffffffcd50) at packet.c:723
#3  0x00007ffff447a7dc in dissector_try_uint_new (sub_dissectors=<optimized out>, uint_val=21, tvb=tvb@entry=0xa32ed0, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, add_proto_name=add_proto_name@entry=1, data=data@entry=0x7fffffffcd50) at packet.c:1188

#4  0x00007ffff4c3078c in decode_tcp_ports (tvb=tvb@entry=0xa32f20, offset=<optimized out>, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, src_port=src_port@entry=21, dst_port=dst_port@entry=3526,   tcpd=tcpd@entry=0x7fffe4034770, tcpinfo=tcpinfo@entry=0x7fffffffcd50) at packet-tcp.c:5250
#5  0x00007ffff4c30b23 in process_tcp_payload (tvb=tvb@entry=0xa32f20, offset=offset@entry=20, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, tcp_tree=tcp_tree@entry=0x0, src_port=src_port@entry=21, dst_port=dst_port@entry=3526, seq=seq@entry=0, nxtseq=nxtseq@entry=0, is_tcp_segment=is_tcp_segment@entry=0, tcpd=tcpd@entry=0x7fffe4034770, tcpinfo=tcpinfo@entry=0x7fffffffcd50) at packet-tcp.c:5322
#6  0x00007ffff4c31117 in desegment_tcp (tcpinfo=0x7fffffffcd50, tcpd=0x7fffe4034770, tcp_tree=0x0, tree=0x0, dport=3526, sport=21, nxtseq=69, seq=1, offset=20, pinfo=0xb42418, tvb=0xa32f20)    at packet-tcp.c:2865
#7  dissect_tcp_payload (tvb=tvb@entry=0xa32f20, pinfo=pinfo@entry=0xb42418, offset=offset@entry=20, seq=<optimized out>, nxtseq=nxtseq@entry=69, sport=21, dport=3526, tree=tree@entry=0x0,     tcp_tree=tcp_tree@entry=0x0, tcpd=tcpd@entry=0x7fffe4034770, tcpinfo=tcpinfo@entry=0x7fffffffcd50) at packet-tcp.c:5389
#8  0x00007ffff4c32c26 in dissect_tcp (tvb=0xa32f20, pinfo=<optimized out>, tree=0x0, data=<optimized out>) at packet-tcp.c:6273

#9  0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe5fa3570, handle=0x7fffe5fa3570, data=0x7fffe3e32030, tree=0x0, pinfo=0xb42418, tvb=0xa32f20) at packet.c:648
#10 call_dissector_work (handle=0x7fffe5fa3570, tvb=0xa32f20, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0x7fffe3e32030) at packet.c:723
#11 0x00007ffff447a7dc in dissector_try_uint_new (sub_dissectors=<optimized out>, uint_val=6, tvb=tvb@entry=0xa32f20, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, add_proto_name=add_proto_name@entry=1,     data=data@entry=0x7fffe3e32030) at packet.c:1188

#12 0x00007ffff488273e in ip_try_dissect (heur_first=<optimized out>, tvb=tvb@entry=0xa32f20, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, iph=iph@entry=0x7fffe3e32030) at packet-ip.c:1976
#13 0x00007ffff488381e in dissect_ip_v4 (tvb=0xa32f70, pinfo=<optimized out>, parent_tree=0x0, data=<optimized out>) at packet-ip.c:2438

#14 0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe4880f60, handle=0x7fffe4880f60, data=0x0, tree=0x0, pinfo=0xb42418, tvb=0xa32f70) at packet.c:648
#15 call_dissector_work (handle=0x7fffe4880f60, tvb=0xa32f70, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0x0) at packet.c:723
#16 0x00007ffff447a7dc in dissector_try_uint_new (sub_dissectors=<optimized out>, uint_val=2048, tvb=0xa32f70, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, add_proto_name=add_proto_name@entry=1,     data=data@entry=0x0) at packet.c:1188
#17 0x00007ffff447a827 in dissector_try_uint (sub_dissectors=<optimized out>, uint_val=<optimized out>, tvb=<optimized out>, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0) at packet.c:1214

#18 0x00007ffff4747123 in dissect_ethertype (tvb=0xb42000, pinfo=0xb42418, tree=0x0, data=0x7fffffffd300) at packet-ethertype.c:262

#19 0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe5ea01f0, handle=0x7fffe5ea01f0, data=0x7fffffffd300, tree=0x0, pinfo=0xb42418, tvb=0xb42000) at packet.c:648
#20 call_dissector_work (handle=0x7fffe5ea01f0, tvb=0xb42000, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0x7fffffffd300) at packet.c:723
#21 0x00007ffff447b9e2 in call_dissector_with_data (handle=<optimized out>, tvb=tvb@entry=0xb42000, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, data=data@entry=0x7fffffffd300) at packet.c:2816

#22 0x00007ffff4745a61 in dissect_eth_common (tvb=tvb@entry=0xb42000, pinfo=pinfo@entry=0xb42418, parent_tree=parent_tree@entry=0x0, fcs_len=0) at packet-eth.c:539
#23 0x00007ffff47465c1 in dissect_eth (tvb=0xb42000, pinfo=0xb42418, tree=0x0, data=0xb1bd28) at packet-eth.c:803

#24 0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe4864dd0, handle=0x7fffe4864dd0, data=0xb1bd28, tree=0x0, pinfo=0xb42418, tvb=0xb42000) at packet.c:648
#25 call_dissector_work (handle=0x7fffe4864dd0, tvb=0xb42000, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0xb1bd28) at packet.c:723
#26 0x00007ffff447a7dc in dissector_try_uint_new (sub_dissectors=<optimized out>, uint_val=1, tvb=tvb@entry=0xb42000, pinfo=pinfo@entry=0xb42418, tree=tree@entry=0x0, add_proto_name=add_proto_name@entry=1,     data=0xb1bd28) at packet.c:1188

#27 0x00007ffff4777914 in dissect_frame (tvb=0xb42000, pinfo=0xb42418, parent_tree=0x0, data=0x7fffffffd710) at packet-frame.c:507

#28 0x00007ffff447a0ef in call_dissector_through_handle (handle=0x7fffe5ea49d0, handle=0x7fffe5ea49d0, data=0x7fffffffd710, tree=0x0, pinfo=0xb42418, tvb=0xb42000) at packet.c:648
#29 call_dissector_work (handle=0x7fffe5ea49d0, tvb=0xb42000, pinfo_arg=0xb42418, tree=0x0, add_proto_name=<optimized out>, data=0x7fffffffd710) at packet.c:723
    ****call_dissector_with_data(frame_handle,...)中的frame_handle是在epan_init中的packet_cache_proto_handles函数中,拿到的"frame"名称的解析器,实际指向的就是dissect_frame函数
#30 0x00007ffff447b9e2 in call_dissector_with_data (handle=<optimized out>, tvb=0xb42000, pinfo=0xb42418, tree=0x0, data=<optimized out>) at packet.c:2816
#31 0x00007ffff447bef6 in dissect_record (edt=edt@entry=0xb42400, file_type_subtype=file_type_subtype@entry=32, phdr=phdr@entry=0xb1bcc0, tvb=tvb@entry=0xb42000, fd=fd@entry=0x7fffffffd8d0,     cinfo=cinfo@entry=0x641140 <cfile+544>) at packet.c:531

#32 0x00007ffff4471884 in epan_dissect_run_with_taps (edt=edt@entry=0xb42400, file_type_subtype=32, phdr=phdr@entry=0xb1bcc0, tvb=0xb42000, fd=fd@entry=0x7fffffffd8d0, cinfo=cinfo@entry=0x641140 <cfile+544>)    at epan.c:378
#33 0x0000000000416994 in process_packet (cf=cf@entry=0x640f20 <cfile>, edt=edt@entry=0xb42400, offset=<optimized out>, whdr=0xb1bcc0, pd=pd@entry=0xb1cf50 "", tap_flags=tap_flags@entry=0) at tshark.c:3440
#34 0x000000000040e67f in load_cap_file (cf=0x640f20 <cfile>, max_byte_count=<optimized out>, max_packet_count=<optimized out>, out_file_name_res=<optimized out>, out_file_type=<optimized out>, save_file=0x0)    at tshark.c:3196
#35 main (argc=<optimized out>, argv=<optimized out>) at tshark.c:1896

看到整个调用关系还是比较复杂的,调用层数比较多,但是随着我们后续的分析会逐渐明朗起来。至此,我们的基本环境已经搭建出来,下一篇文章我们不讲代码,讲讲wireshark的文档,也许会有更大的收获。

4、阅读器

一开始我使用的source insight 3.5作为代码阅读器,但是后来逐渐发现它对一些大的宏定义有兼容问题,例如wireshark中的TRY、ENDTRY、CATCH(x)这些宏定义识别有问题,导致代码阅读出现问题。
后续我迁移到source insight 4.0,大部分没有问题,但是还是会有些许识别问题。
最后改用vim,发现正常了许多。。。

建议大家如果单纯阅读,毕竟vim还是有一些学习成本,直接拿SI 4.0看就好了,影响不会太大,假设强迫症没有那么严重的话。。。

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",
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值