Let's recall the structure of the Modbus frame:
As shown in the image above the functions are positioned in the frame within a 1byte field which we call "function fields".
These functions are identified by a number from 1 to 127. The rest of the values (128 to 255) are used to identify the function that has had a problem during the communication.
As mentioned before, the possible functions supported by the protocol range from 1 to 127 and can be public or private (or user-defined).
Modbus/tcp communication flow
Modbus responses generated by a valid request will have the same structure as the original request, at least up to the function field. Variations can be presented in the (valid) response data of the initial request.
To determine the existence of an implemented function, it's the responses (modbus/tcp) where attention should be focused - specifically on the field value "function". If this value is maintained, it implies that in the structure of the loaded request (to consult a specific function) there are no inconsistencies. This means that the request is valid and that we are dealing with a Modbus function implemented in the device being consulted.
An example of a valid request, using "Function 8" (Diagnostic):
As mentioned beforehand, to determine the existence of an implemented function, you must read the value of the "function" field which displays the responses.
If the value of this field is equal to the sum of the sent function + 128 ("\x80"), it would indicate the existence of a problem, but not which one. To be able to identify the possible exceptions, you need to use the following byte:
Modbus defines the following exception codes:
- "Illegal function"
- "Illegal data address"
- "Illegal data value"
- "Slave device failure"
- "Slave device busy"
- "Memory parity error"
- "Gateway path unavailable"
- "Gateway target device failed to respond"
Finally, based on the error messages, you can determine the modbus functions implemented in industrial devices.
An example of an "invalid" request, using "Function 8" (Diagnostic)
"Recognition of implemented "MODBUS" functions"
Based on the theory presented above, a tool has been developed that automatically allows the researcher to discover the modbus functions that a PLC may have implemented. The tool can be found in the following repo:
usr@pwn:~$ git clone https://github.com/industrialarmy/recon_modbus_functions.gitusr@pwn:~$ cd recon_modbus_functions/usr@pwn:~$ python modbus_functions.py --host <modbus host>
The tests have been reproduced in a controlled environment
Some functions that are within the spectrum of "user-defined" may have non-public exception codes.
Those responses that are valid, but with possible exception codes, their output has been represented in orange. Example:
Final note: sources used for the graphics in the article: