This is a static archive of our old Q&A Site. Please post any new questions and answers at

Getting register values from Modbus/TCP response?


I am parsing a log of a large number of Modbus/TCP transactions and want to grab the raw data that is returned for function code 4 (Read Input Registers). I can grab all the relevant fields for the Modbus/TCP packet except for the register value.

For example, here is the Modbus/TCP portion of a packet which requests a single register read on input register 401 (hex 01 90) :

00 01 00 00 00 06 28 04 01 90 00 01

And the corresponding slave response:

00 01 00 00 00 05 28 04 02 01 0c

The actual value returned for register 401 is the final two bytes of the response: 01 0c (or decimal 268) The status bar at the bottom of wireshark, as well as the display filter, indicates that this value is denoted by modbus.reg16. Looking at the actual modbus section of the packet in Wireshark confirms that register 401 holds the value 268.

However, running tshark on the packet doesn't output 268, but instead it outputs 0.

# tshark -r ./modbus.cap -T fields -e modbus.reg16 100

If I run a similar command on more than one register, the output is a sequential list of numbers starting with 0. For example, running it on a capture with a read request for 8 sequential registers starting at 401 whose contents are various 16-bit integer values:

# tshark -r ./modbus_multiple.cap -T fields -e modbus.reg16 100,101,102,103,104,105,106,107

It is clear that modbus.reg16 uses the register address (reference number) for sorting, but is there any way to access the value returned in that register instead or in addition to this?

Update 02/11/2016

I've uploaded a section of the capture here. The first three requests/responses only request a single register, while the last one requests 8 sequential input registers.

Here's the (abbreviated) output of tshark --version on my system:

TShark (Wireshark) 2.0.1


Compiled (64-bit) with libpcap, with POSIX capabilities (Linux), with libnl 3, with libz 1.2.8, with GLib 2.46.2, without SMI, without c-ares, without ADNS, with Lua 5.2, with GnuTLS 3.4.7, with Gcrypt 1.6.4, with MIT Kerberos, without GeoIP.

Running on Linux 4.4.1-2-ARCH, with locale en_US.UTF-8, with libpcap version 1.7.4, with libz 1.2.8, with GnuTLS 3.4.9, with Gcrypt 1.6.5. Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz (with SSE4.2)

Built using gcc 5.3.0.

I have found a workaround using the pyshark library in Python (which itself issues calls to tshark on the backend). I believe that it just uses raw tshark calls to parse the packet data and store it in various data structures; in this case, it does appear to store the registers as a list of individual key/value pairs. Some example code in the interactive Python 3.5.1 shell:

>>> import pyshark
>>> capture = pyshark.FileCapture('/tmp/mbtcp.pcap')
>>> print(capture[7].modbus.reg16.all_fields[1].showname_key)
Register 101 (UINT16)
>>> print(capture[7].modbus.reg16.all_fields[1].showname_value)
>>> print(capture[7].modbus.reg16.all_fields[7].showname_key)
Register 107 (UINT16)
>>> print(capture[7].modbus.reg16.all_fields[7].shoename_value)

This suits my needs, but it is definitely a workaround. Given sindy’s explanation, it is strange albeit understandable that this is not currently a feature of the dissector. That said, what I’m trying to do could be far enough outside the typical usage of Wireshark that a workaround in the form of an external script or library may be necessary. If it turns out that there truly is no way to do this with tshark, then I may submit an enhancement request at the provided bugzilla link.

asked 10 Feb ‘16, 10:18

evan's gravatar image

accept rate: 0%

edited 11 Feb ‘16, 08:29

The commit that modified that area of the dissector noted:

For decoded register tree objects, use register ‘address’ instead of ‘value’ for the filter field to provide a more useful filter.

I also think it would be useful to get the register data value. It might also be useful to have fields that show the value in any of the usual Modbus formats, e.g. signed\unsigned, 16 bit, 32 bit, float etc.

(11 Feb ‘16, 07:36) grahamb ♦

I am afraid that while for display filtering, it is indeed in most cases more useful to get access to the register “address”, for the actual display at tshark output, the register “value” (or “contents”) is required more often.

As the (pseudo)field identifier (modbus.reg16 in this case) is common for both cases, so is the value to be used for filtering and for display.

But the fact that if “register address” and “register value” cannot be linked together using a display filter leads me to an idea that display of the registers as address:value pairs, such as 100:0,101:4100,102:225,103:225,104:0,105:0,106:65535,107:65535, could be useful.

As backward compatibility seems not to be really important here (as the meaning of modbus.reg16 has changed between dissector releases), if you are going to file the “enhancement” bug, I’d say modbus.reg16.address, modbus.reg16.value, and modbus.reg16.address_value_pair would remove confusion and even allow filtering by specific value of a specific register, like -Y modbus.reg16.address_value_pair = 101:4100. The value format as suggested by @grahamb could be added as another suffix to .value and .address_value_pair.

Looking at that more generically, I’m missing the possibility to use constructions like modbus[2:2], which can be used in the display filter, also as values of the -e parameter to tshark. Correct me if I’m wrong but I believe this would not require modification of the individual dissectors and could be added to tshark as a whole.

@evan, if you happen to know it without googling, can a single query indicate several reference numbers of registers, or only contiguous sets of registers may be read out using a single query? I.e. does my example in the answer with a query for registers with reference numbers 5,9, and 16 make sense?

Also, I’m still puzzled by the fact that your 2.0.1 shows the index of the values (0..7) while both Cloudshark (which I don’t suspect to run on Windows) as well as my Windows version of 2.0.1 properly show 100,101,…, i.e. the address (or reference number if you please), although no protocol preference of either Modbus/TCP or Modbus are related to this.

(11 Feb ‘16, 08:12) sindy

@grahamb Good find on that commit. From the perspective of sorting and displaying, it does make sense to sort that way (as per @sindy’s response).

@sindy Registers can only be polled sequentially given the structure of a modbus request (i.e., you tell it which register to start at and tell it how many to query). It is possible to make custom function codes (which not all devices/implementations support) but I think that trying to do something like what you suggest in your answer would just comprise several individual register reads combined into a single command and thus still fit within spec (and would likely only appear to read separated registers to the software/driver layer and not actually appear as a single modbus request on the packet level.)

As to your last point, I believe I made a mistake somewhere in transcribing the command and will edit my post to reflect this. It is indeed showing the reference number/address when I enter the command as shown above. Sorry for the confusion.

Given all of the comments/responses, does it make sense at this point for me to accept and file a request at the Bugzilla?

(11 Feb ‘16, 08:26) evan

does it make sense at this point for me to accept and file a request at the Bugzilla?

I’m afraid it does.

(11 Feb ‘16, 08:34) sindy

Sounds good, thanks for the help!

(11 Feb ‘16, 08:35) evan

One Answer:


Looking at that on a sample capture as you haven't provided the real one (the one with several registers requested in a single query would be the best), I'm afraid it is a bug of the dissector.

But it is even more complex, which version of Wireshark do you use? Because in your case, the dissector provides the index of the value rather than the value itself, while in Wireshark 2.0.1, it still does not provide the value but it provides the reference number of the register, i.e. you can use a display filter like modbus.reg16 == N where N is the reference number of the register as requested in the corresponding query, not the order number (index) of the value in the response. Can you publish your pcap(ng) file as referred above at some file sharing service and put a link to it to your original question (use edit to do so), so that I could double-check that 2.0.1 is really better than your version in this regard?

I understand the current (2.0.1) behaviour of the dissector in such a way that it does its best to display the reference number of the register and make it available for use in a display filter in the response packet where it does not exist as a protocol field. To do so, the dissector has to maintain state information and render this information taken from the query packet when dissecting the response.

If the assumption is correct, you would have to file an "enhancement" severity level bug at wireshark bugzilla, asking for another field like modbus.reg16.value, to make the value of the register available for display in tshark.

edit: changed the register "addresses" and values in the example below to match the modbus reality.

However, it may still not be all that simple to use even if such enhancement would be implemented, as the display filter only chooses the whole packet, not a particular field in it, and it does not link together the individual expressions. So if the query would ask for registers 5, 6 and 7, and the values of these registers in the response would be 50, 600 and 7000 respectively, use of -Y "modbus.reg16 == 5" -T fields -e modbus.reg16.value would return 50,600,7000, not just 50. Similarly, you would not be able to filter out only packets where the value of register 5 would contain a value of 50, because -Y "modbus.reg16 == 5 and modbus.reg16.value == 50" would display all responses where any of the registers would have a reference number 5 and any of the values would be 50.

answered 10 Feb '16, 16:46

sindy's gravatar image

accept rate: 24%

edited 11 Feb '16, 08:39