# libtshark-core **Repository Path**: quzzzzquopk/libtshark-core ## Basic Information - **Project Name**: libtshark-core - **Description**: tshark封装Java库,报文分析引擎 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 3 - **Created**: 2020-06-18 - **Last Updated**: 2024-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 目录 [0. 简介和展示](#简介和展示) [1. tshark简介和使用](#tshark简介和使用) [2. tshark集成到Java程序中](#tshark集成到Java程序中) [3. tshark缺陷](#tshark缺陷) [4. libtshark-core改进](#libtshark-core改进) [5. 如何使用](#如何使用) [6. 插件扩展](#插件扩展) [后续版本](#后续版本) ## 简介和展示 **libtshark-core**是Java开发的报文分析引擎,支持离线报文解析和在线实时解析,可长时间稳定运行。分析效果如下图所示: ![UI](https://gitee.com/hong_qianhui/libtshark-core/raw/dev/ui/src/main/resources/screenshoot/%E6%88%AA%E5%9B%BE1.png) **libtshark-core**基于tshark模块开发,支持两千多种协议的深度解析,支持lua脚本,C语言扩展,可自定义协议。 ## tshark简介和使用 > 简介 Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。 在过去,网络封包分析软件是非常昂贵的,或是专门属于盈利用的软件。Ethereal的出现改变了这一切。在GNUGPL通用许可证的保障范围底下,使用者可以以免费的代价取得软件与其源代码,并拥有针对其源代码修改及客制化的权利。Ethereal是全世界最广泛的网络封包分析软件之一。(摘自百度百科) `tshark`是`wireshark`的命令行版本,通过命令行参数对`wireshark`运行进行配置,支持`wireshark`的所有功能包括协议的解析。 > wireshark安装 windows环境:windows从官网下载相应的安装安装即可 Linux:Ubuntu16.0.4:`apt-get install wireshark`即可安装 安装过程中要注意:1.要安装wireshark依赖的libpcap/winpcap/npcap库文件。 > tshark安装 windows环境:安装wireshark界面可以选择安装tshark,安装完成后讲tshark配置到环境变量中即可. Linux:Ubuntu16.0.4:`apt-get install tshark`即可安装,注意安装完成后chmod -R 777 tshark修改文件运行权限,否则会导致libtshark-core无法正常启动tshark. 安装完成后测试: console输入`tshark`,若输出`capture on xxx`,表示安装正确,否则安装失败. > 使用 [tshark官网](https://www.wireshark.org/docs/man-pages/tshark.html) 1. 基础(注意:为了方便使用以及运行libtshark-core,请先配置tshark环境变量) ``` tshark -i ``` 上述参数,tshark会以简报的形式打印报文解析信息; 2. 定制 定制tshark输出主要通过`-T ek`、`-e`两个参数设置,前者让tshark以`json`行格式输出报文解析结果,后者可以指定要输出的`json`字段减少输出的信息提高效率。 `tshark -i WLAN -T ek` ```json { "timestamp":"1593648905183", "layers":{ "frame":{ "frame_frame_interface_id":"0", "frame_interface_id_frame_interface_name":"\Device\NPF_{FB2434DC-5AB4-4FCB-A458-998F75263B57}", "frame_interface_id_frame_interface_description":"WLAN", "frame_frame_encap_type":"1", "frame_frame_time":"Jul 2, 2020 08:15:05.183126000 涓浗鏍囧噯鏃堕棿", "frame_frame_offset_shift":"0.000000000", "frame_frame_time_epoch":"1593648905.183126000", "frame_frame_time_delta":"0.033799000", "frame_frame_time_delta_displayed":"0.033799000", "frame_frame_time_relative":"0.072299000", "frame_frame_number":"5", "frame_frame_len":"54", "frame_frame_cap_len":"54", "frame_frame_marked":"0", "frame_frame_ignored":"0", "frame_frame_protocols":"eth:ethertype:ip:tcp" }, "eth":{ "eth_eth_dst":"30:b4:9e:fe:ae:fd", "eth_dst_eth_dst_resolved":"Tp-LinkT_fe:ae:fd", "eth_dst_eth_addr":"30:b4:9e:fe:ae:fd", "eth_dst_eth_addr_resolved":"Tp-LinkT_fe:ae:fd", "eth_dst_eth_lg":"0", "eth_dst_eth_ig":"0", "eth_eth_src":"d0:76:e7:1f:55:70", "eth_src_eth_src_resolved":"Tp-LinkT_1f:55:70", "eth_src_eth_addr":"d0:76:e7:1f:55:70", "eth_src_eth_addr_resolved":"Tp-LinkT_1f:55:70", "eth_src_eth_lg":"0", "eth_src_eth_ig":"0", "eth_eth_type":"0x00000800" }, "ip":{ "ip_ip_version":"4", "ip_ip_hdr_len":"20", "ip_ip_dsfield":"0x00000068", "ip_dsfield_ip_dsfield_dscp":"26", "ip_dsfield_ip_dsfield_ecn":"0", "ip_ip_len":"40", "ip_ip_id":"0x0000aae7", "ip_ip_flags":"0x00004000", "ip_flags_ip_flags_rb":"0", "ip_flags_ip_flags_df":"1", "ip_flags_ip_flags_mf":"0", "ip_flags_ip_frag_offset":"0", "ip_ip_ttl":"50", "ip_ip_proto":"6", "ip_ip_checksum":"0x00008407", "ip_ip_checksum_status":"2", "ip_ip_src":"154.8.190.35", "ip_ip_addr":[ "154.8.190.35", "192.168.0.165" ], "ip_ip_src_host":"154.8.190.35", "ip_ip_host":[ "154.8.190.35", "192.168.0.165" ], "ip_ip_dst":"192.168.0.165", "ip_ip_dst_host":"192.168.0.165" }, "tcp":{ "tcp_tcp_srcport":"443", "tcp_tcp_dstport":"64735", "tcp_tcp_port":[ "443", "64735" ], "tcp_tcp_stream":"0", "tcp_tcp_len":"0", "tcp_tcp_seq":"1", "tcp_tcp_nxtseq":"1", "tcp_tcp_ack":"184", "tcp_tcp_hdr_len":"20", "tcp_tcp_flags":"0x00000010", "tcp_flags_tcp_flags_res":"0", "tcp_flags_tcp_flags_ns":"0", "tcp_flags_tcp_flags_cwr":"0", "tcp_flags_tcp_flags_ecn":"0", "tcp_flags_tcp_flags_urg":"0", "tcp_flags_tcp_flags_ack":"1", "tcp_flags_tcp_flags_push":"0", "tcp_flags_tcp_flags_reset":"0", "tcp_flags_tcp_flags_syn":"0", "tcp_flags_tcp_flags_fin":"0", "tcp_flags_tcp_flags_str":"路路路路路路路A路路路路", "tcp_tcp_window_size_value":"123", "tcp_tcp_window_size":"15744", "tcp_tcp_window_size_scalefactor":"128", "tcp_tcp_checksum":"0x00005639", "tcp_tcp_checksum_status":"2", "tcp_tcp_urgent_pointer":"0", "tcp_tcp_analysis":null, "tcp_analysis_tcp_analysis_acks_frame":"4", "tcp_analysis_tcp_analysis_ack_rtt":"0.033799000", "tcp_analysis_tcp_analysis_initial_rtt":"0.038173000", "tcp_text":"Timestamps", "text_tcp_time_relative":"0.072299000", "text_tcp_time_delta":"0.033799000" }, "_ws_lua_fake":null, "ext":{ "ext_custom_ext_raw_data":"30B49EFEAEFDD076E71F5570080045680028AAE74000320684079A08BE23C0A800A501BBFCDF1110B1613EE03FBA5010007B56390000", "ext_pf_info":"nil" } } } ``` 字段筛选: 比如输出`tcp`协议的`win_size`参数解析结果: `tshark -i WLAN -T ek -e tcp.window_size_value` ```json {"timestamp":"1593649070894","layers":{"tcp_window_size_value":["262"]}} ``` `tshark`会以字符串数组的格式输出解析信息。 **如何获取-e字段名** 以`tcp.window_size_value`为例: ![tshark字段获取](https://images.gitee.com/uploads/images/2020/0702/082056_5f6bf447_7551267.png "屏幕截图.png") 复制**字段名称**即可。 3. 额外参数 除此之外还有一些重要的参数: + -l 禁止缓冲,让`tshark`实时输出解析结果 + -n 禁止网络对象名称解析,tshark会将组播地址解析为multicast格式,可以禁止该模式的解析 + -M tshark默认会将所有的报文缓存在内存中(上下文解析时使用),长时间运行会导致内存不足,将`session_num` 设置到一个合理的参数比如`10000`,让`tshark`进程定时重启清理内存 + -f 过滤器设置,`bfs`格式设置`tshark`捕获报文过滤器 + -Y 协议过滤,只输出指定协议的解析结果 + -X ,添加扩展支持(lua脚本/read_format) ## tshark集成到Java程序中 在`Java`应用中通过`ProcessBuilder`创建子进程运行`tshark`命令即可创建一个`tshark`进程,`tshark`默认会将解析结果通过标准输出输出,所以在`Java`程序中重定向`tshark`标准输出到应用中即可获取到`tshark`进程解析结果。 ```java //简单示例 public class TsharkDemo { /** * 程序运行前需要先安装wireshark,配置tshark环境变量 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //initialize commands List commands = new ArrayList<>(); commands.add("tshark"); commands.add("-i"); commands.add("WLAN"); commands.add("-T"); commands.add("ek"); commands.add("-e"); commands.add("tcp.window_size_value"); //init process starter ProcessBuilder tshark = new ProcessBuilder(commands); //do create process Process process = tshark.start(); try (BufferedReader bfReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { for (;;) { String data = bfReader.readLine(); if (data == null) { break; } System.out.println(data); } }catch (Exception e) { // } } } ``` ![程序输出](https://images.gitee.com/uploads/images/2020/0702/083926_7826f1b1_7551267.png "屏幕截图.png") 利用`json`解析库如`fastjson/jackson/Gson`将输出的`json`字符串转换成`JSONObject`即可。 ## tshark缺陷 基于tshark开发的流量分析还有python语言编写的pyshark,但是pyshark和tshark包括wireshark有一个公有的缺陷,不支持长时间运行。 wireshark(tshark图形界面版本)在长时间运行时会OOM或者程序假死,因为wireshark会将解析完成的报文信息以协议树的格式保存在内存中;除此之外,wireshark在运行时,内部实际开启了两个进程,一是tshark,用于将二进制packet数据解析为指定格式输出如json,xml等,二是dumpcap进程,用于捕获数据包并存储到本地的临时文件,长时间运行会导致该临时文件越来越大,老版本的操作系统本地文件会有2GB限制,达到2GB上限后,tshark进程假死。 ## libtshark-core改进 libtshark-core基于tshark缺陷作了调整, + 支持长时间稳定运行,不会产生大量的临时文件。 + 不会导致内存膨胀程序崩溃。 + 支持tshark在报文解析方面的所有功能。 + Java编写适配,更容易扩展 + 跨平台,支持Linux、Win、Mac ## 如何使用 > 简单示例 ```java public static void main(String[] args) { PacketAnalyzeEngine packetAnalyzeEngine = new PacketAnalyzeEngine(); //数据回调 packetAnalyzeEngine.setDataCallback(new PacketAnalyzeEngine.DataCallback() { @Override public void callback(int processId, String json) { //@param json = tshark解析结果 //@param processId = 分析进程id System.out.println(json); } }); packetAnalyzeEngine.start(); } ``` > 报文分析引擎流程图 > 配置说明 `libtshark-core`通过两个配置文件对程序进行配置,分别是`config/tshark.properties`和`tsharkprocess.json`,具体看配置文件。 ```properties # libpcap filter mac_address=11:22:33:44:55:66 # session reset tshark_session_reset=100000 # 生效的tshark process配置文件名称 tsharkprocess_json=config/tsharkprocess-linux-test.json ``` ```json { //有效配置为1.0 2.0 "version": "2.0", "args": [ { //进程别名 "alias": "测试进程", //这里配置为空即可,表示不过滤协议 "protocol": [ " " ], //要过滤的字段 "filterFields": [ ], //是否将解析成功的字符串输出到console打印 "showConsole": false } ], //tshark抓包口(Windows未虚拟网卡,Linux配置为绝对路径fifo即可) "iFace": [ "/home/hongqianhuis/tshark/fifo/pipeline3" ], //当捕获到该数量报文时会重启tshark进程 "restartProcess": 1000, //true时,不会限制restartProcess,反之会限制最大值和最小值 "debug": true, //"undefinedPacket": "cn.awslquopk.tshark.util.UndefinedPacket", "dumpcap": { //抓包网口 "iFace": "wlp8s0", //存放dump的报文临时文件 "tmpFile": "/home/hongqianhuis/tshark/tmp/tmp", //临时文件切换大小阈值 "fileSize": 100000 }, "clazzMap": { //协议名称 - Java Bean映射 "tcp": "cn.awslquopk.tshark.util.UndefinedPacket" } } ``` > dumpcap "dumpcap": { "iFace": "\\Device\\NPF_{235EDB77-6B88-41E1-8C1E-29DB35754E3D}", "tmpFile": "C:\\Users\\zjucsc\\Desktop\\dumpcap_tmp\\tmp", "fileSize": 100000 } 当前使用的模式是从`dumpcap`的iFace抓包,然后转发到`"arg.iFace对应的网卡中"`分析。(网卡名通过`tshark -D`查看) **要保证tmpFile的路径存在,否则无法启动** > Win建立虚拟网卡 `arg.iFace`最好是虚拟网卡,可以避免其他报文的干扰。 [如何建立虚拟网卡](https://jingyan.baidu.com/article/b87fe19eb3f0f05219356858.html) **上面基本都是对的,就是在最后选择硬件设备的时候要选择Microsoft KM-TEST环回适配器** 虚拟网卡建立成功后就替换掉`arg.iFace`,启动即可。 # Version2.0 2.0版本中,将只使用一个`tshark`进程代替之前的一个协议一个进程,配置内容修改主要有以下几个部分: 0. version ```json5 { "version": "" //2020-06-11 可用的配置为1.0 2.0,如果没有version字段,那么当前默认的版本是1.0 } ``` `2.0`版本包括以下额外配置,`1.0`不用管。 1. args参数修改 ```json5 { "args": [ { "protocol": [ //对应tshark参数中的-Y ,设置为-Y "",表示不过滤协议 "" ], //添加所有协议需要的字段 "filterFields": [ //s7comm "s7comm.param.func", "s7comm.header.rosctr", "s7comm.param.userdata.funcgroup", //tcp //iec104 "104asdu.typeid", "104apci.type", "104apci.utype", //modbus "modbus.func_code", //opcua "opcua.transport.type", "opcua.servicenodeid.numeric", "opcua.RequestHandle", "opcua.nodeid.string", "opcua.MonitoredItemId", "opcua.MonitoredItemIds", "opcua.ClientHandle", "opcua.variant.has_value", "opcua.Boolean", "opcua.SByte", "opcua.Byte", "opcua.Int16", "opcua.UInt16", "opcua.Int32 ", "opcua.UInt32", "opcua.Int64", "opcua.UInt64", "opcua.Float", "opcua.Double", "opcua.datavalue.mask", "opcua.StatusCode", //s7comm-plus "s7comm-plus.data.opcode", "s7comm-plus.data.function", //dnp3 "dnp3.ctl.prm", "dnp3.ctl.prifunc", "dnp3.ctl.secfunc" ], "showConsole": false, "clazz": [ //2.0这里不需要配置 ] } ] } ``` 2. 关闭UndefinedPreProcessor启动 ```json5 { "undefined": false //这里必须设置成false,不然会报错 } ``` 3. 添加classMap 类似于之前`args`中的`clazz`字段,将指定协议与要转换的`packet`之间做映射 这里转换的逻辑写在`PacketCastHandler`里面 ```json5 { "clazzMap": { "s7comm": "com.zjucsc.tshark.packets.S7CommPacket" } } ``` ### 跨平台支持 `Windows`和`Unix系`操作系统都可以创建`NamedPipe`,`tshark`支持从管道读取原始二进制`pcap`或`pcapng`数据 [Windows和Linux管道创建以及数据读取](https://cdimascio.wordpress.com/2014/01/11/named-pipes-with-java/) 测试过程中发现`Windows`下的命名管道不是很方便,且`tshark`从`Windows`管道读取数据的时候经常报错,所以`Windows`环境下目前使用的虚拟网卡方案。 `Linux`下使用的`mkfifo `创建管道,然后`tshark -i /`。 ## 插件扩展 1. 原始数据插件 `wireshark`包括`tshark`原生是不支持获取原始数据的,因此编写如下`lua`脚本用于获取原始数据: ```lua -- @brief A simple post-dissector, just append string to info column -- @author hongqianhui -- https://wiki.wireshark.org/Lua/Examples?action=AttachFile&do=get&target=dissector.lua 《参考》 local ext = Proto("ext","Dummy proto to edit info column") --param1 协议名字 param2 说明 local pf_trasaction_id = ProtoField.new("ext_raw_data", "custom_ext_raw_data", ftypes.STRING) --new一个自定义参数 local pf_info = ProtoField.new("pf_info", "pf_info", ftypes.STRING) --new一个自定义参数 ext.fields = { pf_trasaction_id , pf_info} -- the dissector function callback function ext.dissector(tvb,pinfo,tree) -- local pktlen = tvb:len() -- pinfo.cols.info:append(" --length"..pktlen) -- 在info后面添加数据 local subtree = tree:add(ext,"ext"); -- 在根树后面加一个协议ext,返回该协议下的根节点 subtree:append_text("raw_data"); -- 在自定义的子树上添加说明 --subtree:add(pf_trasaction_id, tvb:range(0, pktlen),"ORZ") -- 在子树上添加自定义的节点,并添加该节点的数据 local ba = tvb:bytes(); subtree:add(pf_trasaction_id, ba:tohex()); -- subtree:add(pf_info, ("%s"):format(cus_str)); end -- register our new dummy protocol for post-dissection register_postdissector(ext) ``` ## 后续版本