私有协议的解密游戏:从秘文到明文
时间:2022-12-06 20:00:01
私有协议
今天的互联网,谁不想创造一个像Ether、IP、TCP这种在网络领域脱颖而出的通信协议。
就连很火的QUIC,也是伴随着RFC落地才正式被公众认可HTTP协议。
因此,大多数人在创建协议时仍然给自己贴上私人协议的标签。这很传统,不传统。
甚至teamviewer这种商业软件会把自己的teamview协议称为私有协议。
私人协议完成工作是你的基本能力。但如果你想让它真正使用,你至少需要做这样的工作,解密游戏的私有协议。
你为什么这么说?因为私人协议在网络中与数据包一起传输,它们必须以字节的形式传输。显然,这是计算机熟悉的规则,而不是人们熟悉的规则。因此,这个解密游戏必须有一个破碎的游戏!
如何打破游戏?熟悉网络的人一定会想到它,是wireshark!
Wireshark
wireshark它是最常用的网络协议分析器,主要用于分析网络上的通信协议,通过分析数据包细粒度观察网络中的事件。
但自定义的私人协议不会被拒绝wireshark支持。太明显了,你没告诉我wireshark,但是wireshark很聪明,它明白会有这样一个重要的需求,所以wireshark有助于提供插件式能力wireshark开发者在wireshark分析他们想要的私人协议。
这点,wireshark 可太懂了!
如今的wireshark支持C和lua语言去定义“如何将私有协议从秘文转换为明文”。因为wireshark提供的API因此,选择任何一种基本能力都能解决解析私有协议的问题。大部分开发,选择lua因为它是一种轻巧小巧的脚本语言,很容易嵌入wireshark当然,在应用程序中tshark也可以-X 选择分析特定的私人协议lua脚本,完成同样的工作。
解密私有协议
最好单独使用一个协议lua脚本解释,在wireshark私有协议可以分为几个部分。
- 协议和字段的定义
- 协议解析器的定义
- 选择导入方式
首先,对协议的全部内容有相应的定义。wireshark提供的API里有Proto、ProtoField、Prefs等几大类的定义,Proto表示协议对象,ProtoField表示协议字段对象,Prefs表示协议表的选项对象。在脚本开始时,应清楚地显示所有需要定义的对象,以便于协议解析器的具体实现。
二是明确定义协议的分析。wireshark默认每一个协议都有一个对应的解析器对象,这个解析器用来解析协议上数据帧的全部字节,并映射到解析树上。解析器对象有三个重要的变量,tvb/buffer表示需要分析的数据缓存本质上是字节数组;pinfo表示在wireshark主窗数据帧信息;tree表示分析树,因为数据包是用树结构分析的,这种结构使wireshark协议层次化。
第三,需要安排协议wireshark中,使得wireshark根据解析器对象定义的规则,找到自定义的协议并同意分析数据包上的对应字节。Ethernet链接层以协议类型区分不同的协议,
IP层亦是通过IP首部的协议字段区分不同的协议,TCP、UDP除了区分协议类型外,传输层协议还使用端口来区分不同的上层协议(但协议类型是区分不同协议的更常见的方法,因此wireshark更希望协议有唯一的固定字段,私有化协议可以通过固定字段注册到相应的前序协议)。
注意:
我们称这个唯一的固定字段为MAGIC,我们希望这个MAGIC是4个字节的ASCII代码,这不仅是为了区分协议,也是为了依捕获过滤器和读取过滤器等工具MAGIC识别协议并进一步处理。
解析策略
wireshark提供了几种不同的分析策略。
解析协议中的字节将定义每个协议。
- dissector 最常用的分析器
- heuristic dissector 启发式解析器
- post dissector 后序解析器
- chained dissector 链式解析器
第一种分析器是最常用的分析器。通常没有协议特征匹配,只是根据协议上的字节映射相应的内容。这适用于单个私人协议,可以依赖wireshark只需注意定义协议、字段和解析器即可实现自己的分析表。
解析器:Proto.dissector(tvb,pinfo,tree) 注册:DissectorTable:add(port/field,Proto)
第二种分析器,启发式分析器。不同的协议可以根据条件来判断。在分析前,根据不同的条件提前过滤不满意的协议,使私人协议能够使用更多的证据来证明其独特性,非常适合在公共协议上注册,可以防止类似的协议混淆(公共协议注册意味着向ip、tcp、udp在此类公开协议上注册)。
解析器:condition_function(tvb,pinfo,tree) Proto.dissector(tvb,pinfo,tree) 注册:DissectorTable:register_heuristic("protoname",condition_function)
第三种分析器,后序分析器。所有分析器处理后,每个数据帧统一处理。wireshark每个数据帧都将由一层解析器处理,不需要在任何协议上注册。
解析器:Proto.dissector(tvb,pinfo,tree) 注册:register_postdissector(Proto)
第四种分析器,链式分析器。这种分析器可以基于现有分析器的扩展能力,而不需要重写新的分析器。与后序分析器一样,它在所有分析器处理后进行分析。这种分析器有一个优点,不需要从头开始处理没有扩展分析器处理的数据包。
解析器:Proto.dissector(tvb,pinfo,tree) 注册: do ... end / DissectorTable:add(port/field,Proto)
后序和链式分析器
这里解释一下后序解析器和链式解析器,因为它们最终都生效了,但它们有自己的使用场景。后序分析器是一种新的分析器。这种分析器最终处理每个数据包。当有新的字段设置或新的特征匹配并需要进一步处理时,后序分析器非常适用;链式分析器更像是一种裹原始分析器的装饰器。所有分析器处理后,可修改/添加/删除原分析器处理的字段,适应新的进化协议。
分析器及分析表
解析器是Dissector,每个协议都需要定义一个负责协议字段分析的专用分析器;分析表是DissectorTable,主要用于存储注册分析器,负责调用分析器处理后续工作payload。当每个协议注册到不同前序协议的分析表时,本质上是注册分析器。在上一层协议分析完成后,您将在自己的分析表中找到相应的注册分析器。特征匹配后,只有满足条件才能调用分析器。
wireshark的DissectorTable有几种类型,但只有两种定制,一种是整形分析表,另一种是字符串分析表,两者的区别是注册分析器,注册方法是字节整形手术还是字符串。
//解析表 local table = DissectorTable.new("dissectorTablename","DissectorTable-filter",ftypes.UINT16,base.HEX,Proto)
协议注册到解析表上,解析表就有了解析器。
function Proto.init() DissectorTable.get("dissectorTablename"):add(0x1111,Proto) end
这样,有解析表的协议可以根据协议后的两个字节判断是否有注册的解析器。
local identity = buf(0,2):uint() local next_protocol = table:get_dissector(identity) // 判断是否有注册协议 if next_protocol then ///隐式调用分析器根据字节数组的整体特征分析后面的字段 table:try(identity,buf,pinfo,tree) end
最后
最后,教你一个简单直接的调用方法。如果只有一两个协议,可以直接调用解析器而不创建分析表。
原因很简单,因为解析器定义为全局函数。
Dissector.get("protoName):call(buf,pinfo,tree)
最后的最后,wiresahrk上别忘了注册脚本!脚本注册在init.lua。
do_file(.....proto_file.lua)