Pythonを使ってパケットからModbus/TCPのレスポンスデータ抽出

パケットデータの解析

Scapyライブラリを利用します。このライブラリは非常に便利で、お手軽にパケットを解析したり生成することができます。

注意:しかし、Scapyライブラリはポートスキャンなども行えるため注意が必要です。ポートスキャン自体は不正アクセスにはならないとは思いますが、会社など第三者環境でポートスキャンを行うと、IDS(侵入検知システム)で攻撃と検知されて大事に至る可能性もあります。

Scapyライブラリのインストール

下記コマンドで一発インストールできます。

$ sudo pip3 install scapy-python3

Modbus/TCPのレスポンスデータの抽出

scapy.contrib.modbusAPIscapy.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]