Pythonを使ってパケットからModbus/TCPのレスポンスデータ抽出
パケットデータの解析
Scapyライブラリを利用します。このライブラリは非常に便利で、お手軽にパケットを解析したり生成することができます。
注意:しかし、Scapyライブラリはポートスキャンなども行えるため注意が必要です。ポートスキャン自体は不正アクセスにはならないとは思いますが、会社など第三者環境でポートスキャンを行うと、IDS(侵入検知システム)で攻撃と検知されて大事に至る可能性もあります。
Scapyライブラリのインストール
下記コマンドで一発インストールできます。
$ sudo pip3 install scapy-python3
Modbus/TCPのレスポンスデータの抽出
scapy.contrib.modbusAPIのscapy.contrib.modbus. ModbusADUResponseクラスを使用します。
Modbus/TCP情報を含むパケットデータは適当に用意してください(笑)
以下にサンプルコードを示します。
#!/usr/bin/env python # -*- coding: utf-8 -*- import scapy.all as scapy import scapy.contrib.modbus as mb for pkt in scapy.PcapReader("modbus.pcapng"): if mb.ModbusADUResponse in pkt: pkt.show()
mobus.pcapngというパケットデータを読み込んで、Modbus/TCPのレスポンスデータのみをshow関数で表示するという簡単なコードです。
Modbus/TCPが標準ポート502以外を使用している場合
標準ポートの502を使っている場合はこのコードでも問題ないのですが、502以外の場合はscapy.packet.bind_layers関数を使用してポート番号を追加します。 以下のサンプルコードは、ポート505を追加した例です。
#!/usr/bin/env python # -*- coding: utf-8 -*- import scapy.all as scapy import scapy.contrib.modbus as mb from scapy.layers.inet import TCP #scapy.packet.bind_layers(TCP, mb.ModbusADUResponse, sport=502) scapy.packet.bind_layers(TCP, mb.ModbusADUResponse, sport=505) for pkt in scapy.PcapReader("modbus.pcapng"): if mb.ModbusADUResponse in pkt: pkt.show()
抽出例
[ Read Holding Registers Response ]の箇所がregisterValが機器から送られてきたレジスタ値となります。
###[ Ethernet ]### dst = xx:xx:xx:xx:xx:xx src = yy:yy:yy:yy:yy:yy type = IPv4 ###[ IP ]### version = 4 ihl = 5 tos = 0x0 len = 61 id = 15625 flags = DF frag = 0 ttl = 63 proto = tcp chksum = 0x996f src = 172.18.141.28 dst = 172.18.128.1 \options \ ###[ TCP ]### sport = 502 dport = 55381 seq = 2888026032 ack = 3684924240 dataofs = 5 reserved = 0 flags = PA window = 502 chksum = 0x6a5e urgptr = 0 options = [] ###[ ModbusADU ]### transId = 0x80cb protoId = 0x0 len = 15 unitId = 0x1 ###[ Read Holding Registers Response ]### funcCode = 0x3 byteCount = 12 registerVal= [18602, 58880, 0, 0, 0, 0] ###[ Ethernet ]### dst = xx:xx:xx:xx:xx:xx src = yy:yy:yy:yy:yy:yy type = IPv4 ###[ IP ]### version = 4 ihl = 5 tos = 0x0 len = 61 id = 6258 flags = DF frag = 0 ttl = 63 proto = tcp chksum = 0xbe06 src = 172.18.141.28 dst = 172.18.128.1 \options \ ###[ TCP ]### sport = 505 dport = 55383 seq = 53992196 ack = 3614244534 dataofs = 5 reserved = 0 flags = PA window = 502 chksum = 0x54c urgptr = 0 options = [] ###[ ModbusADU ]### transId = 0x80cc protoId = 0x0 len = 15 unitId = 0x1 ###[ Read Holding Registers Response ]### funcCode = 0x3 byteCount = 12 registerVal= [18598, 23424, 0, 0, 0, 0]