一. 问题描述
所提供的压缩包是某工控业务网络中的实际捕获的通信数据包。请你发现并找出其中所有的Modbus/TCP包。仅仅针对Modbus数据包分析如下几点:
- 发现Modbus通信的Master节点地址与相对应的slave节点地址。有几组master-slave? 请绘制业务包中的Modbus基本通信拓扑结构。
- 请解析Modbus报文。以几组典型的Modbus通信为例,解析Modbus访问-应答机制的通信。
- 请绘制此业务Modbus中的通信数据的时间序列图。要求覆盖数据包中出现的所有的master与slave及其节点.
二. 分析过程
- 发现Modbus通信的Master节点地址与相对应的slave节点地址。有几组master-slave? 请绘制业务包中的Modbus基本通信拓扑结构。
1.使用wireshark打开.pcap文件, 设置过滤器规则为modbus,然后右上角文件->导出为csv文件,便于我们读取与分析.
2.编写python程序解析csv文件,详情见AnalysisCSV.py
主要思想为: 若为query请求则判为master节点,若为Response包则判为slave节点,并加入已有master节点所管理的slave节点列表中.运行结果为:
假设所有数据包无误, 故有四组master-slave,其拓扑结构为:
注:考虑到192.168.1.13地址应归属于Slave, (根据ip分配顺序的推断)但是其却发送query报文,故疑似该节点受到攻击, 此报文为攻击报文,请求是写多个线圈值.
故正确的拓扑结构应为:
3. 请绘制此业务Modbus中的通信数据的时间序列图。要求覆盖数据包中出现的所有的master与slave及其节点.
1.使用wireshark将数据包 导出为json格式, (导出方式见第一问).此问使用json格式进行分析.具体详见modbus.json
2.编写分析代码AnalysisJson.py
大致思想为:逐项读取json文件,如果发现是slave节点,则将这个包所携带的寄存器值信息压入所对应的Master类中的reg列表中.
最后绘制图表.(运行后,鼠标放在点上面,右上角就会出现time和value值).输入为寄存器值和时间(此处的时间格式是16:49:55-569031000,调试代码可以发现. 点数太多,所以绘制的图表横坐标都粘连在了一起, 因为点太多,散点图还是无法确切判断寄存器数值变化情况,如果要详细查看,源代码中注释的部分(64~67行)可以打印出寄存器值和时间的列表值.)
Slave:192.168.1.10 (太多了,点右边小箭头可以收起来)
Register0:
Register16:
Register14:
Register16:
Slave:192.168.1.12:
Register0:
Register10:
Register12:
Register16:
三. 程序源码
1. AnalysisCSV.py
import csv
class Master:
def __init__(self, s):
self.src = s
self.slave = []
def pushslave(self, s):
self.slave.append(s)
def main():
filename = "./modbus.csv"
master = []
srclist = []
with open(filename) as f:
render = csv.reader(f)
header_row = next(render)
print(header_row)
for row in render:
src = row[2]
dst = row[3]
info = row[6]
if info.find("Query:") != -1:
if src not in srclist:
srclist.append(src)
m = Master(src)
master.append(m)
if info.find("Response") != -1:
for i in master:
if dst == i.src:
if src not in i.slave:
i.pushslave(src)
# ii += 1
# if (ii == 10000):
# break
print("\n\n\n------------------搜索结束,打印出所有的master与对应的slave信息:-----------------------")
for mst in master:
print("master:")
print(mst.src)
print("slave:")
for slv in mst.slave:
print(slv)
print("----------------------------")
if __name__ == '__main__':
main()
2. AnalysisJson.py
import json
import jsonpath
import matplotlib.pyplot as plt
class Slave:
def __init__(self, src):
self.src = src
self.reg = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
self.time = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
def draw(h, ll):
plt.figure(figsize=(16, 8))
plt.xlabel("Time :Oct 15, 2019 from 16:46:10-000000000 to 16:49:55-569031000")
plt.ylabel("The value of register")
plt.title("the slave.ip.src is in console, the register number also is in console")
plt.scatter(h, ll)
plt.show()
def main():
jsondata = json.load(open('modbus.json', encoding='utf-8'))
slavecheck = ['192-168-1-10', '192-168-1-11', '192-168-1-12']
srclist = []
slavelist = []
# print(jsondata)
for pack in jsondata:
src = jsonpath.jsonpath(pack, '$._source.layers.ip.ip-src')
src = src[0]
if src in slavecheck:
if src not in srclist:
srclist.append(src)
slave = Slave(src)
slavelist.append(slave)
for sl in slavelist:
if sl.src == src:
newtime = pack["_source"]["layers"]["frame"]["frame-time"]
newtime = newtime[13:30]
# time = jsonpath.jsonpath(pack, '@._source.layers.frame.frame-time')
modbus = pack['_source']['layers']['modbus']
for modbusitem in modbus:
if 'Register' in modbusitem:
regnumber = int(modbusitem[9:11])
regvalue = int(modbusitem[21:])
sl.reg[regnumber].append(regvalue)
sl.time[regnumber].append(newtime)
print("\n\n-------------------------------search result------------------------------------------------")
# print(slavelist)
for slaveitem in slavelist:
print("slave src:(The dots are replaced by horizontal bars)")
print(slaveitem.src)
i = 0
for regi in slaveitem.reg:
if len(regi) > 0:
print("the register number:" + str(i))
draw(slaveitem.time[i], slaveitem.reg[i])
# print("the register value list:")
# print(slaveitem.reg[i])
# print("the time list:")
# print(slaveitem.time[i])
i += 1
print("--------------one slave has been drawed--------------")
if __name__ == '__main__':
main()