在工控领域,我们会遇到许多协议,为了进行安全研究,经常需要对协议的具体内容进行探索,今天我们就来聊聊关于工控协议逆向的问题。

接下来会简单介绍一下常用的协议逆向方法并配合一些实战,当然,从未知到已知的探索过程不仅仅需要代码上的实践,还需要数学上的分析与建模,所以在这几篇文章中不仅会有工控、协议的知识,还有大量的数学内容,因为我本身不是搞学术研究的,所以一些东西也只是略微了解而已。

按照分类,工控协议一般可以分为以下两种:

  • 公开协议,这里的公开主要是说它是公开发表并且无版权要求的,我们介绍的modbus就属于这一种。
  • 私有协议,顾名思义就是厂家自有的,为正式公开的,我们介绍过的西门子S7comm就属于这一种。

但不论是公开协议还是私有协议往往都具有一定的未知性。像是modbus,虽说大部分信息我们都是了解的,但是还有很多function code是厂商自己偷偷用的,像是施耐德我们之前就提过,有自己的0x5a来实现一堆高权限操作;S7comm这类的私有协议就更不用说了,要不是前辈们的逆向工程,我们其实是对协议内容一无所知的。我们今天要聊的就是对于这类未知的逆向过程。

对于协议的逆向我们也是分成两类方法:

  • 基于网络轨迹的逆向,即对抓取的流量包进行分析,利用各类数学分析、推理,对数据进行切分、关系预测、生成状态机等等,从而推断出协议的部分内容、进行fuzz等操作。
  • 基于接收端程序的逆向,即对协议数据的接收端程序进行逆向分析,从而得到协议的内容,这也是现在常用的方法,像是最近S7commPlus的逆向就是借助分析上位机的OMSp_core_managed.dll组件来实现的。

当然,这两种逆向方式都需要结合相应的设备进行调试来完成,也完全可以两种方式结合,先基于网络轨迹大致判断协议格式、关系,再通过逆向程序加以完善。

一、基于网络轨迹的逆向

考虑到我们为入门教程,所以我们选用的数据包并不是真正的”未知“协议,我们使用系列第一篇文章的modbus数据包来进行分析,我们将其TCP以上均视为未知部分。

如图的包,我们将modbus部分的数据视作:

x00x00x00x00x00x04x00Zx00x02

现在看上去还是毫无头绪?没关系,我们一点点来。

确定协议字段的基本知识

确定协议字段说白了就是根据流量包中的大量流量对比,”猜“出来哪些数据应该是一个字段,这个过程中涉及到协议分析算法,而这些算法又是由一些重要的数学模型、算法构成的,我选取了《Network Protocol Analysis using Bioinformatics Algorithms》、《基于网络协议逆向分析的远程控制木马漏洞挖掘》两篇论文中的某些部分进行简要说明,来大致了解一下这部分理论知识(因为我们是要做实际分析,所以涉及到研究部分的算法等理论我就不再细化了)。

LD(LevenShtein Distance),假设我们有A、B两个字符串,A经过插入、删除、替换字符的最短过程变为B,经过的步骤表示两个字符串的差异。如:

A = "modbus"
B = "modicon"

显然我们需要把子串”bus“换为”ico“,然后添加字符”n“,所以LD(A,B)=4,python实现代码如下:

def normal_leven(str1, str2):len_str1 = len(str1) + 1len_str2 = len(str2) + 1# 创建矩阵matrix = [0 for n in range(len_str1 * len_str2)]# init x轴for i in range(len_str1):matrix[i] = i# init y轴for j in range(0, len(matrix), len_str1):if j % len_str1 == 0:matrix[j] = j // len_str1for i in range(1, len_str1):for j in range(1, len_str2):if str1[i - 1] == str2[j - 1]:cost = 0else:cost = 1# 若ai=bj,则LD(i,j)=LD(i-1,j-1)   取矩阵对角的值# #若ai≠bj,则LD(i,j)=Min(LD(i-1,j-1),LD(i-1,j),LD(i,j-1))+1   在对角,左边,上边,取最小值+1matrix[j * len_str1 + i] = min(matrix[(j - 1) * len_str1 + i] + 1, matrix[j * len_str1 + (i - 1)] + 1,matrix[(j - 1) * len_str1 + (i - 1)] + cost)return matrix[-1]str1 = ''
str2 = ''
a = normal_leven(str1, str2)
print(1-a/max(len(str1), len(str2)))
print(type(1-a/max(len(str1), len(str2)))

LCS(Longest common subsequence),这个和大家理解的两个字符串求最大子序列有些不同,这里的字符并不一定要连续出现。如:A与B的最长子序列为”mod“,所以LCS(A,B)=3,而我们将A变为”m o d b u s“,LCS(A,B)仍然为3,所以,如果LCS(X,Y)=0,那么立即推,X、Y没有任何一个字符相同。在论文中包括了GSA(全局序列对比)与LSA(局部序列对比),GSA用在对协议域的理解上,而LCS则是对相似序列进行聚类。编程中,我们可以使用Needleman/Wunsch算法来求解。

我们还是以上面的两个字符串为例:

首先我们将矩阵初始化,即上述表格,接着按照公式进行填充

若ai=bj,则LCS(i,j)=LCS(i-1,j-1)+1

若ai≠bj,则LCS(i,j)=Max(LCS(i-1,j-1),LCS(i-1,j),LCS(i,j-1))

该公式其实非常简单,如果行列字符一样,则填充的值为它左上角的值加1,如果不一样就是左上角、上边、左边的最大值。按照这个标准我们从第一行开始填,得到如下结果:

然后从右下角进行回溯操作,若ai=bj,则走左上角;若ai≠bj,则到左上角、上边、左边中值最大的单元格,相同的话优先级按照左上角、上边、左边的顺序,直到左上角为止。最终按照如下规则写出表达式(_表示插入字符或者是删除字符操作)

  • 若走左上角单元格,A+=ai,B+=bi
  • 若走到上边单元格,A+=ai,B+=_
  • 若走到左边单元格,A+=_,B+=bi

多序列对比,实际上是LCS的扩展,采用引导树、非加权成对群算术平均法等来进行,非加权成对群算术平均法即UPGMA算法,这是一种聚类算法,在我们前面已经得到的距离的基础上进行操作,将距离最小的两个节点进行聚合,然后再次计算新的节点间的距离,最终生成演化树。

演化树(Phylogenetic tree),在多序列对比中生成的树,其实就像是生物进化图那样,由根衍生出一个一个节点,如图所示为DNA系统的演化树,我们所要建立的演化树是关于协议的数据的,通过这种方式来寻找协议流量中的相似部分。

建树的方法主要有两大类,一类是基于距离的,一类的基于character的。我们上面提到的就是基于距离的建树方式,一般来讲,基于距离的方法将数据抽象为距离,从而有了较快的处理速度,但是其抽象过程中会有信息量的损失;而基于character的方法是在一个已有的模型上建立的,所以需要有一个靠谱的模型,但好处就是我们的信息不会丢失。

广义后缀树(Generalized Suffix Tree),用来实现求解最大公共子串、匹配字符串、找重复串等等,这里说的最大公共子串就是我们平时理解的:连续的、相同的字符串。比如”modbus“和”m od i c o n“,那么这俩的最大公共子串就是od而不是上面的”mod“。该技术常用于病毒的特征码提取。下面举个栗子来解释一下:

假设我们现在有modbus、modicon两个字符串,对于modbus,后缀有:

s
us
bus
dbus
obus
modbus

我们将其按字典序排序,然后建立树,根节为空,每个字母为一个节点,从根到叶子就对应了一个单词,如图所示:

我们还可以对单节点链条进行压缩,当然这个单词所有的链条都是单节点的……然后我们将modicon也按后缀,然后插入到这个树中,重合的部分即为公共子串(因为我不知道怎么画图,所以你们就自行想象一下吧)。

聚类(cluster),大白话就是分类,一是用来对流量进行粗略分类,二是用来对提取的字符串进行分类。具体涉及的包括了k紧邻算法、关键词树算法等等

以上是一些基础性的问题,有了以上的基础,我们可以大致将流量包的分析归为以下几步:

  • 粗略聚类,提取主要分析的流量,并将相似的流量首先分到一起
  • 采用各类算法来对字段进行划分
  • 根据某些字段再次进行聚类
  • 对一类的流量进行关系分析

二、Netzob划分数据

netzob是一种基于网络轨迹的逆向工具,目的就是为了分析未知协议,当然还有其他的一些,比如PI等等,我们这里就以netzob为例进行操作。

首先当然是要安装,netzob需要大量的前置包,安装很麻烦,很有可能遇到各种错误,因为和实际环境有关,所以我也没法全部列举出来,大家安装时自行尝试吧。

apt-get install python-dev      #提前需要安装的库
apt-get install python-impacket
apt-get install python-setuptools
apt-get install libxml2-dev
apt-get install libxslt-dev
apt-get install gtk3
apt-get install graphviz
git clone https://github.com/netzob/netzob.git
cd netzob
python3 setup.py develop --user  #开发者友好模式

以上步骤完成后我们就可以在python3中import了。

from netzob.all import *

当然也可以python3 setup.py install来安装友好的图形化界面,使用./netzob即可打开,但是在我的机器上出现了问题,大家可以自行尝试。如果实在是安装不成功的同学,官方也给了docker镜像。

docker pull netzob/netzob
docker run --rm -it -v $(pwd):/data netzob/netzob  #pwd为当前位置,挂载到根目录下的data

搞定后我们就可以开始干活了。

m = PCAPImporter.readFile("modbus.pcap").values()

该条语句用来导入我们的流量包,我们可以查看一下它的说明。

参数主要关注两个,一个是importLayer,这是指定我们要分析的data是在哪一层,以我们modbus来说,是基于tcp的,所以data就相当于是tcp往上一层,所以填5,如果是S7comm呢则要考虑你要分析哪一层再进行选择;另一个是bpfFilter(Berkeley Packet Filter),也即是伯克利包过滤的意思,这是一种语法,可以指定你要选择哪些流量,如下所示:

host 0.0.0.0 and (port 138) #筛选出ip为0.0.0.0且端口为138的流量包

接着我们进行符号化,即筛选出所有相似的流量,这里就涉及到了我们之前提到的数学知识

s = Symbol(messages = m)

可以看到提取出来的就是相似的流量就是我们流量包中的modbus部分,这一步就相当于我们上面提到的粗略聚类,Netzob将modbus部分的流量提取了出来,并将这些流量放到了一起。但是现在我们还是啥也看不出来,我们希望能够对data再次进行分析,对比得到哪些字符应该是一块的,哪些是分开的。

Format.splitStatic(s)

该方法用来将我们的data根据相似性与静态分布规律,划分为几个Field,当然,我们也可以通过“肉眼观察法”使用splitDelimiter(symbol,ASCII(“Z”)来进行人工的划分。这一步相当于“采用各类算法来对字段进行划分”,也是核心部分,如图即为划分Field后的symbol。

我们就以第一组为例,打开wireshark来检查一下分析的结果:

真正的划分如下:

'x00x00'  | 'x00x00' | 'x00x04'  | 'x00Z' | 'x00x02'

可以看到差距较大,观察流量包后发现,主要原因是因为modbus的前四个字段在该流量包中区分不太“明显“,以第一个字段举例,modbus用了两个字节表示事务标识符(即Transaction identifier ),但在这些流量中最多最多就是x00x01,根本就没有用到低字节,所以在划分时被认为是两个字段了。同理,长度字段也是如此,该流量包中的length也没有用到低字节,所以也被划为了两个字段。

知道了情况我们就可以对症下药了,一是我们引入新的流量包,选取一些数据量较大、情况足够全面的流量,可以稍加完善;二是我们通过不断的尝试和日常积累进行手动划分,比如每种协议基本都有的length字段、标识字段等等进行手动划分。但是由于协议本身的规定与限制(比如,虽然给了两个字节,但是实际上并没有使用低字节,厂商只是为了扩展或者对齐),我们只可能完善,但绝不可能完美划分字段,不过仅仅是这样对我们的帮助已经很大了。

之后我们要进行的步骤为“根据某些字段再次进行聚类”,但是这里由于我们对于哪个是关键字段并不清楚,所以暂时放弃这一步。

接下来我们要做的工作就是要猜测字段的含义,当然我们通过这种方式绝对不可能“猜”出来“Z”是施耐德专用的功能码,我们能做的是推理这些字段之间的关系。

for symbol in symbols.values():rels = RelationFinder.findOnSymbol(symbol)print("[+] Relations found: ")for rel in rels:print("  " + rel["relation_type"] + ", between '" + rel["x_attribute"] + "' of:")print("    " + str('-'.join([f.name for f in rel["x_fields"]])))p = [v.getValues()[:] for v in rel["x_fields"]]print("    " + str(p))print("  " + "and '" + rel["y_attribute"] + "' of:")print("    " + str('-'.join([f.name for f in rel["y_fields"]])))p = [v.getValues()[:] for v in rel["y_fields"]]print("    " + str(p))

RelationFinder为我们提供了不同的关系分析方法,这里我们选择使用基于符号的分析方法,也就是对我们之前进行过Field划分的符号进行分析。

当然,这只能探索符号内部的关系,像是我们的数据包,我们只能发现length字段,但是已经是非常大的进步。为我们之后进行测试、逆向程序都省下了不少功夫。

当然这也是我们协议分析的第一步,在之后的文章中我们将基于这部分内容,继续进行探索。

工控安全PLC固件逆向一相关推荐

  1. 工控安全PLC固件逆向三

    之前我们详细分析了bootram和Vxworks的基本启动流程,这篇文章中我们把视线转到plc的网络部分,同时来复现我们第一个.第二个工控安全漏洞. 一.VxWorks的网络设备驱动 一般我们说有三种 ...

  2. 工控软件/PLC的信息远传的简单解决方案

    工控软件/PLC的信息远传的简单解决方案 针对现在的物联网趋势,很多小伙伴想迈出工控信息远传这一步,但是又会出于对软件行业的陌生而望而却步.本人有10年工控经验以及物联网平台开发.对接经验,这里分享一 ...

  3. 1、零基础学工控——初识plc

    ☞☞☞点击查看更多优秀工控PLC博客☜☜☜ 大家好我是你们的朋友JamesBin,从今天开始我们将一起学习西门子的PLC这个自动化工具,这节课就让我们来一起看看什么是PLC吧! 初识plc 什么是PL ...

  4. 特斯拉面试,工控经典PLC题目:一键启动功能实现解法分析,少个坑,给自己多个机会

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.问题分析: 二.模拟运行 三.还有没有更简单的 前言 PLC 面试经典题目,一键启动功能:单个按钮按下一次控制灯亮 ...

  5. JamesBin专属工控PLC博客导航

    hello大家好,我是你们的朋友JamesBin,这篇文章主要是为了给大家进行博客导航的,方便大家的查找,下面是博客的全部连接,当然博客还在持续更新中,我会把最新的博客放到本博客中,希望大家能够关注我 ...

  6. 《Linux操作系统 - RK3288开发笔记》第1章 吉方工控G-3288-02固件更新

    最近在研究吉方工控推出的G-3288-02开发板,该开发板使用的处理器是RK3288,和大家熟知的Firefly-RK3288是一样的处理器,只是板子的外设些许不同,今天我就带着大家一起来探索这块优秀 ...

  7. 工控安全:攻防演示案例分享

    列举一些可以在工控安全项目中用的攻防演示案例,持续更新...... 前言 在某核电的安全项目中,感谢公司leader信任让我负责项目的总体方案设计.攻防案例设计.安全防护等方案,经过前期大量的搜索.复 ...

  8. 西门子、施耐德、三菱、RA:全球主要工控协议及端口解析

    写在前面 大家好,我是小智,智能制造之家号主~ 说到工业协议,前面我们已经原创了很多: 最全整理工业通讯上的领域各种总线+协议+规范+接口-数据采集与控制 最详细的工业网络通讯技术与协议总结解读(现场 ...

  9. 工控危险 施耐德PLC产品现高危漏洞

    本文讲的是 工控危险 施耐德PLC产品现高危漏洞,施耐德电气公司开始发布固件补丁处理影响该公司莫迪康(Modicon)M340可编程逻辑控制器(PLC)产品线的高严重性漏洞. 工业控制系统网络应急相应 ...

最新文章

  1. CS231n 2016 通关 第三章-SVM 作业分析
  2. adb devices检测不到夜神模拟器的解决办法
  3. 反射获取成员变量并改值
  4. ocs 2007技巧:查看存档服务记录的消息内容
  5. Centos7:mysql5.6安装,配置及使用(RPM方式)
  6. mask属性是css3的吗_使用CSS3 mask(蒙版,遮罩)属性实现超酷按钮悬停动画
  7. easyui datagrid url不请求请求_Go Web编程--深入学习解析HTTP请求
  8. 【转】 C++中的new VS C语言中的malloc
  9. Hadoop(HDFS,YARN)的HA集群安装
  10. 零元学Expression Blend 4 - Chapter 9 用实例了解布局容器系列-「Canvas」
  11. stata学习之空间权重矩阵制作
  12. ORAN专题系列-5:5G O-RAN 一体式小基站硬件白盒化的参考架构
  13. 计算几何——扇形面积
  14. 豆瓣电影top250爬虫+数据可视化分析
  15. android水波纹教程,Android实现简单水波纹效果
  16. Swagger3/thinkphp6教程
  17. 关于JavaScript面向对象
  18. 详解数据模型设计方法
  19. 瀚高CEO苗健:用开源软件改变中国基础软件产业格局
  20. ESP8266-002 ESP8266EX简介

热门文章

  1. 苹果cms v10模板 海螺模板V15 已去授权 源码+安装教程 长期更新
  2. 03-jQuery事件绑定和解绑
  3. java实现PDF转图片功能,附实例源码!
  4. echarts 饼状图 (数据为0或很小的扇形显示问题、扇形间隔)
  5. postman后端返回的数据显示中文乱码解决
  6. 论文阅读------Stochastic Gradient Descent with Differentially Private updates
  7. spss数据处理--数据检查
  8. 服务器android打包,在服务器上使用 gradle 打包 android 源码
  9. Tsi721芯片驱动代码使用说明
  10. java 中文大写金额_java编写的金额转中文大写