导语:sharkpy是基于wireshark以及libpcap用于解析、分析、网络数据包的python模块。sharkpy中的解析模块继承了wireshark中的tshark命令,并且对它进行了修改。sharkpy中的数据注入以及pcap文件写入是调用了libpcap的函数。
Sharkpy中包含了搜索,创建,修改数据,发送数据,将数据包写入到新的pcap文件中这五个模块。这些会在一个python程序或者一个python会话中全部完成。
1. sharkPy.file_dissector:使用wireshark中的解析库从文件中捕获数据包,并且将捕获的数据包传递给python文件中,作为要分析的对象。 2. sharkPy.write_dissector:从网卡中捕获数据包,并且通过wireshark中的解析库将数据包进行解析。然后将数据包作为python对象,进行调用。 3. sharkPy.file_writer:将修改或者未修改的数据包写入到新的pcap文件中。举个例子,我们可以使用sharkPy.file_dissector捕获数据包,将捕获的数据创建到一个新的数据包中,然后将这个数据包写入到新的文件中。 4. sharkPy_wire_writer:使用libpcap库函数将任意数据包写入到任意网卡通信当中。在使用sharkpy过程中,这个模块是用于构造符合我们要求的传输数据包。 5. sharkPy.utils:函数通用模块 6. sharkPy.protocol_blender:特定协议选择模块,目前包含了ipv4,以及ipv4上的tcp通信。
sharkPy是使用的NSA原版,不确定是否存在后门,所以请谨慎使用。
设计目标
1. 将解析的数据包数据作为poython对象进行调用。 2. 在python环境中提供这些功能,无论是在python程序中,或者在脚本环境下都可以进行调用。 3. 当使用者执行命令时,会在后台进行,而不会使命令行崩溃。 4. 将众多数据包模块集成到了几个命令当中。 5. 熟悉wrireshark以及python的用户可以很容易的上手 6. 使用尽可能少的C代码进行连接wrireshark函数库。
什么是sharkPy?
SharkPy目标是成为一个将wireshark的功能进行更细,更模块化,更容易使用到其余项目当中的库文件。从这个目标可以看出,SharkPy不只是将wireshark和python结合起来,而是进行极大的优化。
第一步实现的是可以在wireshark环境之外将wireshark提供的功能编译成python模块,提供给用户调用。这一功能已经在众多linux发行版实现。第二步就是继续拓展到更多linux版本以及windows版本中,并且提高工具的稳定性。功能完成之后,sharkPy就可以使用wrieshark的全部功能,并且功能模块按上述六个模块进行划分。
安装
Linux中进行安装
只需要安装了wireshark2.0.1以上版本的就可以使用以下命令进行安装:
## ubuntu-16.04-desktop-amd64 -- clean install sudo apt-get git git clone https://github.com/NationalSecurityAgency/sharkPy sudo apt-get install libpcap-dev sudo apt-get install libglib2.0-dev sudo apt-get install libpython-dev sudo apt-get install wireshark-dev #if you didn't build/install wireshark (be sure wireshark libs are in LD_LIBRARY_PATH) sudo apt-get install wireshark #if you didn't build/install wireshark (be sure wireshark libs are in LD_LIBRARY_PATH) cd sharkPy sudo ./setup install
在docker中安装
1.初始化
第一步,创建SharkPy目录,然后将dockerfile放到目录中.cd到这个目录。
然后使用下面命令:
docker build -t "ubuntu16_04:sharkPy" .
注意:
在编译过程中需要等待一段时间,最后它会自动结束。 sharkpy的源码应该在SharkPy文件夹中。 上述编译会创建Ubuntu镜像,然后将Sharkpy作为python模块进行安装。
2.作为docker容器运行
使用下面命令可以进行交互式使用:
docker run -it ubuntu16_04:sharkPy /bin/bash
使用下面命令会允许工具捕获网络流量:
docker run -it --net=host ubuntu16_04:sharkPy /bin/bash
SharPy的API
从文件中解析数据包:
dissect_file(file_path,options=[],timeout=10):当使用get_next_from_file函数时,从数据包文件中选取数据包。
参数以及使用说明:
1. 流量文件所在位置,以及文件名 2. 收集以及解析流量的选项。这些选项有:disopt_DECODE_AS,以及disopt.NAME_RESOLUTION。 3. timeout:打开文件等待时间,如果超过,则报错。 4. 函数返回值为一个元组:(p,exit_event,shared_pipe);p是解析过程处理程序,exit_event是数据处理程序定义的程序出口点,shared_pipe是分离器解析的网络共享管道。
注意:用户不应该直接使用这些返回值,而是这些返回值作为get_next_from_file以及close_file函数的参数进行使用。
get_next_from_file(dissect_process,timeout=None):得到下一组可用的数据包。
参数及使用说明:
1.dissect_process:在dissect_file函数返回的数据元组。 2.这个函数的返回值是数据包中构建的通信树的头节点
close_file(dissect_process):停止,清除痕迹
参数以及使用说明
1. dissect_process:在dissect_file函数返回的数据元组 2. 返回值为空
注意,close_file函数必须在每个会话中都是使用。
从网卡中解析数据包
dissect_wire(interface,options=[],timeout=None):当使用get_next函数时,会从网卡中获得数据包。
参数以及使用说明:
1.interface:选择要获取数据的网卡 2.options:获取以及解析的选项,选项有: disopt.DECODE_AS, disopt.NAME_RESOLUTION, and disopt.NOT_PROMISCUOUS. 3.函数返回一个元组:(p,exit_event,shared_queue),p是当前解析数据程序,exit_event是数据处理程序定义的程序出口点,shared_queue,分离器返回的数据包解析树的队列
注意,在使用过程中同样不能直接使用这个函数的返回值,而是同上,这些返回值会作为参数传入到get_next_from_wire以及close_wire函数中。
get_next_from_wire(dissect_process,timeout=None):从存活的数据包中解析下一个可用的数据包。
参数以及使用说明:
1.dissect_process:在dissect_file函数返回的数据元组。 2.返回值为数据包中构建的通信树的头节点
close_wire(dissect_process):停止以及清理存活的数据包
参数以及使用说明:
1. dissect_process:在dissect_file函数返回的数据元组 2. 返回值为空
注意:close_wire必须在每个会话中调用。
将数据或者数据包写入到网卡或文件中
wire_writer(write_interface_list):wire_writer对象构造器。将任意数据写入到网卡当中。
参数以及使用说明:
1. write_interface_list:将要写入数据的网卡列表。 2. 返回值为wire_writer对象。 3. wire_writer.cmd:对writer对象执行命令。 4. wr.cmd(command=wr.WRITE_BYTES, command_data=data_to_write, command_timeout=2) 5. wr.cmd(command=wr.SHUT_DOWN_ALL, command_data=None, command_data=2) 6. wr.cmd(command=wr.SHUT_DOWN_NAMED, command_data=interface_name, command_data=2) 7. wire_writer.get_rst(timeout=1):返回元组(success/failure, number_of_bytes_written)
file_writer():创建一个新的file_writer对象,将数据包写入到pcap文件当中。
make_pcap_error_buffer():创建一个正确大小,以及初始化的错误缓冲区,返回值为错误缓冲区。
pcap_write_file(output_file_path,error_buffer):创建以及打开一个新的pcap文件。
参数说明:
1.output_file:输出文件的路径。 2.error_buffer:在make_pcap_error_buffer函数中的返回值。 3.返回值:ctypes.c_void_p,是其他写入函数所需要的对象。
pcap_write_packet(context,upper_time_val,lower_time_val,num_bytes_to_write,data_to_write,error_buffer):将数据写入打开的pcap文件当中
参数说明:
1. context:pcap_write_file()函数返回的对象。 2. upper_time_val: 数据包分组的时间单位。这个值是从效用函数get_pkt_times()返回的元组第一个值中获得。 3. lower_time_val:数据包剩余处理时间。这个值是从效用函数get_pkt_times()返回的元组第二个值中获得。 4. num_bytes_to_write:写入到文件的字节数目,数据缓冲区的长度。 5. data_to_write:要写入到数据缓冲区的内容。 6. err_buffer:make_pcap_error_buffer()函数返回的错误缓冲区。任何错误信息都会写入到这个缓冲区中。 7. 如果成功,返回值为0,如果失败返回值为-1。错误信息可以通过err_buffer查看。
pcap_close(context):必须在每个会话中被调用,用于写入缓冲区中,关闭写入文件,以及释放内存。
参数说明:
1. context:pcap_write_file()返回值 2. 返回值为空。
通用模块
do_funct_walk(root_node,funct,aux=None):将数据包中的通信树通过递归传递给函数中。采用深度遍历。
参数说明:
1. root_node:通信树中要被第一个访问的节点。 2. funct:要调用的函数 3. aux:可选的辅助变量将作为每个被调用的函数的参数 4. 函数返回值为空。
get_node_by_name(root_node,name):查找并且返回具有你要查找内容的节点。
参数说明:
1. root_node:传入参数的头节点 2. name:你要搜索的内容。 3. 函数的返回值是对你要查找内容,因为内容可能不是唯一,所以返回的是一个列表。
get_node_data_details(node):返回给定节点的数据元组。
参数说明:
1. node表示你要获取的节点信息 2. 返回值为:tuple(data_len,first_byte_index, last_byte_index, data, binary_data).data_len表示node节点数据长度,first_byte_index表示在数据包中这个节点开始的地址,last_byte_index表示数据包中这个节点结束的地址,data表示节点的数据。binary_data:以二进制形式表示的节点数据。
get_pkt_times(pkt=input_packet): 返回数据包所包含的时间戳信息。
参数说明:
1. pkt:从sharkPy解析路径之一中返回的数据包解析树。 2. 返回值为:(epoch_time_seconds, epoch_time_nanosecond_remainder),这两个返回值作为file_writer的参数。
find_replace_data(pkt, field_name, test_val, replace_with=None,
condition_funct=condition_data_equals, enforce_bounds=True,
quiet=True):进行搜索,匹配,并且将数据包中的内容进行替换。
参数说明:
1. pkt:从sharkPy解析路径之一中返回的数据包解析树。 2. field_name:要查找的内容 3. test_val:用于进行比较匹配功能的数据变量或者缓冲区 4. replace_with:将查找的数据进行替换的内容 5. condition_funct:bool类型的函数,并且格式为:condition_funct(node_val,test_val,pkt_dissection_tree)。默认函数为 condition_data_equals(),如果node_val==test_val,那么这个函数就会返回true。这就是字节匹配的意思。 6. enforce_bounds:如果被设置为True,就会强制执行len(replace_with) == len(node_data_to_be_replaced). 7. quiet:如果设置为False,那么如果找不到要查找的字符串,那么就会把错误信息输出到stdout中。 8. 如果找到字符串,那么返回修改过后的数据包。如果找不到,则返回null。
condition_data_equals(node_val, test_val, pkt_dissection_tree=None):一个匹配函数,可以传递给find_replace_data().
参数说明:
1. node_val:要查找的来自数据包的值 2. test_val:与node_val进行比较的值 3. 如果node_val==test_val,那么返回值为真。反之,为假。
condition_always_true(node_val=None, test_val=None,
pkt_dissection_tree=None):一个匹配函数,可以传递给find_replace_data().这一函数总会返回真,这一函数的唯一作用就是判断目标字段是否在解析数据包中。
协议选择
ipv4_find_replace(pkt_dissection, src_match_value=None,
dst_match_value=None, new_srcaddr=None, new_dstaddr=None,
update_checksum=True,
condition_funct=sharkPy.condition_data_equals),修改特定的ipv4协议中的字段。
参数说明:
1. pkt_dissection:数据解析树。 2. src_match_value:当前寻找的源Ip(以16进制表示)。该值将会被替换。 3. dst_match_value:当前寻找的目的IP(以16进制表示)。该值将会被替换。 4. new_srcaddr:用该值替换上述的src_match_value 5. new_dstaddr:用该值替换上述的dst_match_value 6. update_checksum:如果在默认情况下,即为真,就会启动ipv4修复校验。 7. condition_funct:匹配函数,用于查找正确的数据包进行修改。
tcp_find_replace(pkt_dissection, src_match_value=None,
dst_match_value=None, new_srcport=None, new_dstport=None,
update_checksum=True,
condition_funct=sharkPy.condition_data_equals):通过ipv4修改tcp选择的字段。
参数说明:
1. pkt_dissection:数据解析树。 2. src_match_value:当前寻找的源tcp端口寻找(以16进制表示)。该值将会被替换。 3. dst_match_value:当前寻找的目的tcp端口(以16进制表示)。该值将会被替换。 4. new_srcaddr:用该值替换上述的src_match_value 5. new_dstaddr:用该值替换上述的dst_match_value 6. update_checksum:如果在默认情况下,即为真,就会启动ipv4修复校验。 7. condition_funct:匹配函数,用于查找正确的数据包进行修改。
用法实例
捕获文件中的数据包:
目前支持的选项是DECODE_AS和NAME_RESOLUTION
import sharkPy in_options=[(sharkPy.disopt.DECODE_AS, r'tcp.port==8888-8890,http'), (sharkPy.disopt.DECODE_AS, r'tcp.port==9999:3,http')] #
开始文件读取以及数据包解析
dissection = sharkPy.dissect_file(r'/home/me/capfile.pcap', options=in_options)
使用sharkPy.get_next_from_file获取读取的数据包:
>>> rtn_pkt_dissections_list = [] >>> for cnt in xrange(13): ... pkt = sharkPy.get_next_from_file(dissection) ... rtn_pkt_dissections_list.append(pkt) Node Attributes: abbrev: frame. name: Frame. blurb: None. fvalue: None. level: 0. offset: 0. ftype: 1. ftype_desc: FT_PROTOCOL. repr: Frame 253: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) on interface 0. data: 005056edfe68000c29....<rest edited out> Number of child nodes: 17 frame.interface_id frame.encap_type frame.time frame.offset_shift frame.time_epoch frame.time_delta frame.time_delta_displayed frame.time_relative frame.number frame.len frame.cap_len frame.marked frame.ignored frame.protocols eth ip tcp Node Attributes: abbrev: frame.interface_id. name: Interface id. blurb: None. fvalue: 0. level: 1. offset: 0. ftype: 6. ftype_desc: FT_UINT32. repr: Interface id: 0 (eno16777736). data: None. Number of child nodes: 0 ...<remaining edited out>
在最后关闭会话:
>>> sharkPy.close_file(dissection)
取一个数据包树,并且将他们的名字作为其索引。
>>> pkt_dict = {} >>> sharkPy.collect_proto_ids(rtn_pkt_dissections_list[0], pkt_dict)
以下就是检索该数据包中所有的key:
>>> print pkt_dict.keys() ['tcp.checksum_bad', 'eth.src_resolved', 'tcp.flags.ns', 'ip', 'frame', 'tcp.ack', 'tcp', 'frame.encap_type', 'eth.ig', 'frame.time_relative', 'ip.ttl', 'tcp.checksum_good', 'tcp.stream', 'ip.version', 'tcp.seq', 'ip.dst_host', 'ip.flags.df', 'ip.flags', 'ip.dsfield', 'ip.src_host', 'tcp.len', 'ip.checksum_good', 'tcp.flags.res', 'ip.id', 'ip.flags.mf', 'ip.src', 'ip.checksum', 'eth.src', 'text', 'frame.cap_len', 'ip.hdr_len', 'tcp.flags.cwr', 'tcp.flags', 'tcp.dstport', 'ip.host', 'frame.ignored', 'tcp.window_size', 'eth.dst_resolved', 'tcp.flags.ack', 'frame.time_delta', 'tcp.flags.urg', 'ip.dsfield.ecn', 'eth.addr_resolved', 'eth.lg', 'frame.time_delta_displayed', 'frame.time', 'tcp.flags.str', 'ip.flags.rb', 'tcp.flags.fin', 'ip.dst', 'tcp.flags.reset', 'tcp.flags.ecn', 'tcp.port', 'eth.type', 'ip.checksum_bad', 'tcp.window_size_value', 'ip.addr', 'ip.len', 'frame.time_epoch', 'tcp.hdr_len', 'frame.number', 'ip.dsfield.dscp', 'frame.marked', 'eth.dst', 'tcp.flags.push', 'tcp.srcport', 'tcp.checksum', 'tcp.urgent_pointer', 'eth.addr', 'frame.offset_shift', 'tcp.window_size_scalefactor', 'ip.frag_offset', 'tcp.flags.syn', 'frame.len', 'eth', 'ip.proto', 'frame.protocols', 'frame.interface_id']
请注意,pkt_dict是全部列出的,所以每个字段并不是唯一的。
>>> val_list = pkt_dict['tcp']
发现tcp列表中只有一个元素,如下所示:
>>> for each in val_list: ... print each ... Node Attributes: abbrev: tcp. name: Transmission Control Protocol. blurb: None. fvalue: None. level: 0. offset: 34. ftype: 1. ftype_desc: FT_PROTOCOL. repr: Transmission Control Protocol, Src Port: 52630 (52630), Dst Port: 80 (80), Seq: 1, Ack: 1, Len: 0. data: cd960050df6129ca0d993e7750107d789f870000. Number of child nodes: 15 tcp.srcport tcp.dstport tcp.port tcp.port tcp.stream tcp.len tcp.seq tcp.ack tcp.hdr_len tcp.flags tcp.window_size_value tcp.window_size tcp.window_size_scalefactor tcp.checksum tcp.urgent_pointer
使用以下命令进行快捷查找节点:
>>> val_list = sharkPy.get_node_by_name(rtn_pkt_dissections_list[0], 'ip')
数据包中每一个节点都包含属性以及子孩子列表:
>>> pkt = val_list[0]
以下是访问属性的方式:
>>> print pkt.attributes.abbrev tcp >>> print pkt.attributes.name Transmission Control Protocol
这是pkt的子孩子列表:
>>> print pkt.children [<sharkPy.dissect.file_dissector.node object at 0x10fda90>, <sharkPy.dissect.file_dissector.node object at 0x10fdb10>, <sharkPy.dissect.file_dissector.node object at 0x10fdbd0>, <sharkPy.dissect.file_dissector.node object at 0x10fdc90>, <sharkPy.dissect.file_dissector.node object at 0x10fdd50>, <sharkPy.dissect.file_dissector.node object at 0x10fddd0>, <sharkPy.dissect.file_dissector.node object at 0x10fde50>, <sharkPy.dissect.file_dissector.node object at 0x10fded0>, <sharkPy.dissect.file_dissector.node object at 0x10fdf90>, <sharkPy.dissect.file_dissector.node object at 0x1101090>, <sharkPy.dissect.file_dissector.node object at 0x11016d0>, <sharkPy.dissect.file_dissector.node object at 0x11017d0>, <sharkPy.dissect.file_dissector.node object at 0x1101890>, <sharkPy.dissect.file_dissector.node object at 0x1101990>, <sharkPy.dissect.file_dissector.node object at 0x1101b50>]
获取有关解析的节点数据中有用的信息:
>>> data_len, first_byte_offset, last_byte_offset, data_string_rep, data_binary_rep=sharkPy.get_node_data_details(pkt) >>> print data_len 54 >>> print first_byte_offset 0 >>> print last_byte_offset 53 >>> print data_string_rep 005056edfe68000c29....<rest edited out> >>> print binary_string_rep <prints binary spleg, edited out>
从网络中获取数据包,以及解析数据包
在SharkPy的wire_dissector中提供了额外的 NOT_PROMISCUOUS选项。
>>> in_options=[(sharkPy.disopt.DECODE_AS, r'tcp.port==8888-8890,http'), (sharkPy.disopt.DECODE_AS, r'tcp.port==9999:3,http'), (sharkPy.disopt.NOT_PROMISCUOUS, None)
开始捕获和解析。注意调用者必须有执行权限。但是如果以root权限可能会有危险:
>>> dissection = sharkPy.dissect_wire(r'eno16777736', options=in_options) >>> Running as user "root" and group "root". This could be dangerous.
使用sharkPy.get_next_from_wire得到数据包并且对他们进行解析:
>>> for cnt in xrange(13): ... pkt=sharkPy.get_next_from_wire(dissection) ... sharkPy.walk_print(pkt) ## much better idea to save pkts in a list
一定要关闭捕捉会话:
>>> sharkPy.close_wire(dissection)
将数据或者数据包写入到网络中
使用网卡名字创建写入对象
>>> wr = sharkPy.wire_writer(['eno16777736'])
将timeout设置为2,并且向网络中发送数据:
>>> wr.cmd(wr.WRITE_BYTES,' djwejkweuraiuhqwerqiorh', 2)
检查是否发生错误。如果成功,那么返回数据:
>>> if(not wr.command_failure.is_set()): ... print wr.get_rst(1) ... (0, 26) ### returned success and wrote 26 bytes. ###
将数据包写入到pcap文件当中。
创建写入文件对象:
>>> fw = file_writer()
产生错误缓冲区:
>>> errbuf = fw.make_pcap_error_buffer()
打开或者生成新的pcap文件,准备下一步写入:
>>> outfile = fw.pcap_write_file(r'/home/me/test_output_file.pcap', errbuf)
在现有的数据包解析数据:
>>> sorted_rtn_list = sharkPy.dissect_file(r'/home/me/tst.pcap', timeout=20)
将第一个数据包写入到pcap文件当中:
得到第一个解析数据:
>>> pkt_dissection=sorted_rtn_list[0]
获取写操作所需要的数据包信息:
>>> pkt_frame = sharkPy.get_node_by_name(pkt_dissection, 'frame') >>> frame_data_length, first_frame_byte_index, last_frame_byte_index, frame_data_as_string, frame_data_as_binary = sharkPy.get_node_data_details(pkt_frame[0]) >>> utime, ltime = sharkPy.get_pkt_times(pkt_dissection)
将数据包写入到文件中:
>>> fw.pcap_write_packet(outfile, utime, ltime, frame_data_length, frame_data_as_binary, errbuf)
关闭输出文件,并且清理数据:
>>> fw.pcap_close(outfile)
下面程序就是在写新的数据包之前匹配和替换数据,然后输出pcap文件
import sharkPy, binascii test_value1 = r'0xc0a84f01' test_value2 = r'c0a84fff' test_value3 = r'005056c00008' fw = sharkPy.file_writer() errbuf = fw.make_pcap_error_buffer() outfile = fw.pcap_write_file(r'/home/me/test_output_file.pcap', errbuf) sorted_rtn_list = sharkPy.dissect_file(r'/home/me/tst.pcap', timeout=20) for pkt in sorted_rtn_list: # do replacement new_str_data = sharkPy.find_replace_data(pkt, r'ip.src', test_value1, r'01010101') new_str_data = sharkPy.find_replace_data(pkt, r'ip.dst', test_value2, r'02020202') new_str_data = sharkPy.find_replace_data(pkt, r'eth.src', test_value3, r'005050505050') # get detains required to write to output pcap file pkt_frame = sharkPy.get_node_by_name(pkt, 'frame') fdl, ffb, flb, fd, fbd = sharkPy.get_node_data_details(pkt_frame[0]) utime, ltime = sharkPy.get_pkt_times(pkt) if(new_str_data is None): new_str_data = fd newbd = binascii.a2b_hex(new_str_data) fw.pcap_write_packet(outfile, utime, ltime, fdl, newbd, errbuf) fw.pcap_close(outfile)