背景
工作上由于需要对一些DPI的规则进行维护,很多时候需要对一些非标准协议进行逆向分析,从而找到非标准协议中的可识别内容。例如典型的是各种游戏协议。
因此多多少少接触到了一些协议的逆向分析工作。协议逆向目前并没有一个靠谱的工具可以使用,主流的思路还是使用大量的数据,进行自动化对比分析+人工干预。
本文总结一下平时在协议逆向上的一些心得。
协议逆向简介
一个协议中包含数据报文和控制报文,对于单个报文,分析最主要的工作就是把报文分割成不同的域,每个域都有自己的长度,名称,意义,数据类型,各个域的类型,长度,相互关系,时序就构成了协议的语法。
协议逆向本身比较依赖先验经验,以及对数据的敏感,在协议逆向工作前加强对核心数据的收集,能显著提升逆向的成功率。总体而言,这是一个比较依赖个人经验和技巧的工作,以下介绍为基本思路。
协议逆向场景
1,网络对抗
2,恶意软件分析
3,漏洞挖掘,其中自动化挖掘漏洞主要依靠模糊测试,模糊测试分为白盒模糊测试,基于模型的模糊测试,基于反馈的模糊测试其中基于模型的模糊测试主要是利于文件格式和协议格式构造测试用例,所以也依赖于协议分析。
4,协议重用,例如跨平台的文件共享samba就是逆向了微软的smb协议
5,网络管理,比如家庭防火墙的应用识别和应用拦截功能
协议逆向的几种方式:
协议格式提取技术包括基于网络流量的协议逆向,和基于执行轨迹的协议逆向,基于交互式的协议逆向方式。
基于网络流量的协议逆向
基于网络流量的协议逆向有以下几个局限性
①在仅有正例的情况下,无法得到绝对准确的协议格式
②对于加密或则压缩后的协议,无法逆向
③分析结果依赖于样本覆盖(比如游戏协议的分析,可能需要区分不同平台,不同角色,不同区服等等的样本)
但是有以下优点
①时效性强,因为属于静态分析,成本比较低,快速得出结果
②对终端依赖比较小,不需要获得客户端程序
③通用性比较强
在实际的应用识别规则提取中,用的比较多的也是这种方式。详细逆向思路下下文。
基于执行轨迹的协议逆向
基于执行轨迹的协议逆向,一般是需要获取二进制文件,对二进制文件进行逆向,获取协议格式或者加密密钥等。如果二进制文件可以执行,可以进行动态调试。由于在实际的应用识别规则提取工作中,针对的都是apk,都做了混淆和加壳处理,逆向难度很大,这方面我也不擅长。包括逆向工作实际耗时很多,也不适合在应用识别的规则提取工作中使用,所以不做描述。
基于交互式的协议逆向
交互式的协议逆向在目前实际工作中,是作为基于网络流量协议逆向的补充。这种方式的前提是可以通过发送指定数据包的方式与服务器交互,根据服务器的返回内容来确定数据包的协议格式。这个步骤目前可以通过手动/自动化的方式进行。详细逆向思路下下文。
如何基于网络流量进行协议逆向
目前工作中用到的主要是基于网络流量的协议逆向分析方式。前提条件是先对需要逆向协议的APP进行抓包,抓包的适合需要配合实际使用场景。如针对一款游戏进行抓包的时候,要抓完整的从启动—登录—进入游戏的全过程。
另外由于基于网络协议这种方式的特性,抓包的时候尽量覆盖多种不同场景,例如不同的平台(android/ios),不同的登录id,不同的版本之间进行比对。
基本的流程如下图,根据需要,可以选择使用自动化系统,或者直接手动进行。
由于自动化输出结果后,也需要人工进行核对,调整,所以这里主要介绍人工分析的方式和思路。
第一步,定位需要分析的核心数据流
可以根据以下参数从pacp中定位需要进行协议逆向的核心数据流,直接将pcap导入脚本运行既可输出。如果要人工分析的话,也需要参考一下参数。
# print '数据流时间%s' % str(tcp_time)
# print '数据包数量%s' % str(len(pkt_list))
# print '大包比例%s' % str(largepkt_proportion)
# print '发送数据包平均大小%s' % str(srcavg)
# print '返回数据包平均大小%s' % str(repavg)
# print '发送数据包最小值%s' % str(srcmin)
# print '返回数据包最小值%s' % str(repmin)
# print '发送包高延迟数量%s' % str(srcdelaynum)
# print '返回包高延迟数量%s' % str(repdelaynum)
# print '平均包间隔%s' % str(avgdelay)
# print '平均发送包间隔%s' % str(avgintervalsrc)
# print '平均返回包间隔%s' % str(avgintervalrep)
第二步,从抓包场景和抓包文件中确定关键字
关键信息,一种是抓包场景下的信息,包括网络环境信息等。如客户端的ip,端口号,服务端的ip,端口号,协议发生时的时间戳等信息。具体获取哪些指定信息,有一个预设的配置文件。获取成功后,将这些信息加入关键信息库1。
例如针对一条数据,可以获得该条数据的关键信息如下:
客户端ip: 192.168.137.151
服务端ip:106.14.176.31
客户端端口: 60857
服务端端口:16100
协议发生时的时间戳:1609210534
另外一种是在pcap文件中包含的关键信息,这些信息是从其他数据流中提取出来的。提取方式可以通过脚本,用tf-idf算法直接输出结果,也可以人工提取。这些信息可能包含在协议中,来进行协议逆向的时候,猜测语义的时候优先考虑这些信息。
例如针对数据包通过机器学习算法提取出来的关键数据如下:
username=qzPicd
vuin=310290686
version=3.1.1.2232
platform=30403
第三步:先确定常见的协议格式字段
分隔符字段,用来标识可显示字符串的结束
长度字段:取值为后续某一字段或者复合字段的字节长度
计数字段:取值为后续某一复合字段中元素重复次数
关键字字段:声明报文,复合字段或字段的类型(例如hello,request)
格式标识字段:指示后续某一复合字段的格式
校验和
IP网络地址
协议实体身份识别,也就是cookies
主机名
文件名,路径名
网络端口
时间戳
data,协议实体使用的数据,可能就是单纯的用户数据
这些字段相对来说比较好确定,如果有满足条件的先确定这些字段,从而得到协议的初步格式。自动分析的方式可以参考findlenth.py中的逻辑。手动的话,比较依赖个人经验。
第四步:通过序列对比和交互式的协议逆向,进一步分析协议格式以及每一个字段语义/取值范围
序列对比,这个自动化脚本会直接输出结果。如果手动进行的话,需要预先挑选出两个相同场景下的协议内容,比较典型的如下:
27:74:00:00:48:00:00:00:00:00:02:00:00:00:0d:00:00:00:1c:00:00:00:00:00:00:00:ba:00:00:00:28:32:31:36:38:65:34:61:39:36:36:33:30:36:64:63:66:34:65:30:33:31:32:65:31:34:30:66:35:32:30:63:39:35:33:31:37:62:34:30:66:00:00:00:04:00:00:00:22
27:74:00:00:48:00:00:00:00:00:02:00:00:00:0e:00:00:00:1f:00:00:00:00:00:00:00:be:00:00:00:28:32:31:36:38:65:34:61:39:36:36:33:30:36:64:63:66:34:65:30:33:31:32:65:31:34:30:66:35:32:30:63:39:35:33:31:37:62:34:30:66:00:00:00:04:00:00:00:22
这两个协议在14:1 18:1 25:1 三个位置有区别,根据之前进行的常见协议格式字段分析,可以得知这三个字段比较可能是控制字段,或者可变的递增字段。
再结合实际抓包时候的场景,比如这两个协议内容不同可能是因为平台不同?用户id不同?操作场景不同?渠道号不同?版本不同?等等,最终可以人工分析出来14:1 18:1 25:1 这三个位置,是代表版本号的字段。
补充交互式的协议逆向
这个步骤主要是在完成了上面步骤后,已经有了初步的协议格式,不过有部分语义或者部分字段的取值范围无法确定。如果这个时候服务器处于一个可以交互的状态,那么可以通过手动制造数据包的方式,探测服务器返回,通过服务器返回来确定字段的语义,以及取值范围。
例如一段未知的网络协议,客户端发送的数据包A如下:
3366000800081001000000000100000052000000000203000000ff000000651002030000001d6f30466e5876726b326b525a53734a4e4e7047326b63496e4f5a4b3100000000000000000000000100000000
发送这段网络数据后,服务器响应的数据包B是:
336600080008100201000000010000003000000010021048614a41394858517638476b5556573800000004000000000061c1d9b1276ba954cb3d4b438e236251
首先针对发送的数据包A,简单划分区域,针对每一个区域,将原有数据更换为其他数据。
如A开头的336600,变更为000000,则重新构建数据包A1为
0000000800081001000000000100000052000000000203000000ff000000651002030000001d6f30466e5876726b326b525a53734a4e4e7047326b63496e4f5a4b3100000000000000000000000100000000
将数据包A1发送到服务器。获取服务器结果的时候发现服务器没有响应此请求。
则初步认定或者更加确定336600为协议的定义字段,此协议固定以336600开头,如果不以336600开头,则服务器不会做出响应。
同理,通过此方式可以确定其他的字段。比如怀疑一处字段为用户的cookies,那修改cookies后,服务器预期的返回应该是可以响应,但是用户数据相关的data段为空,或者是返回一段报错信息。如果发送后返回符合了预期,那么可以确定之前的语义推测是正确的。
第五步:对比场景、倒退到场景,核实逆向结果的准确性
这一步的目的是确定之前分析的协议格式、语义的正确性。由于应用识别和应用拦截都需要很高的准确度,如果针对于协议的某一个字段的格式、语义分析错误,可能会导致无法识别,或者更严重的出现大范围的误识别。
比如之前外网出现过提取的规则被mqtt协议误命中的问题,由于mqtt协议在安卓手机上使用还比较广泛,出现了用户反馈,就是典型的协议逆向结果不准确,导致识别规则出现误命中的情况。
这一步主要工作,是把上面流程中完成初步分析的结果,跟实际的场景做一个核实,查看是否存在冲突,增强协议逆向结果的可信度。
这部分工作比较依赖于经验和技巧,具典型的例子如下
初步认定版本信息相关的协议字段,需要跟pcap中提取出来的版本信息做核对
初步认定为用户cookies的,需要配合登录用户相关信息做核对
初步认定为渠道相关信息的,需要根据不同的渠道包信息进行核对。