#!/usr/bin/python3

import socket
from time import sleep
import sys

transaction_identifier = b"\x18\x03"	
protocol_identifier = b"\x00\x00"	
lenght_field = b"\x00\x06"			
mod_function = b"\x03"

exception_code = b"\x83"

options = [
		['address','','Target address to be attacked.'],
		['port','502','Target port to be attacked. Default: 502'],
		['id','1','Slave\'s id, value between 1 and 256. Default: 1'],
		['register','','Register to be read, value between 1 and 65535.']	
	]
	
def show_info():
	print("This module can be used to read a specified Holding Register from the slave.")

def exception(code):
	return {
		b"\x01" : "ILLEGAL FUNCTION",
		b"\x02" : "ILLEGAL DATA ADDRESS",
		b"\x03" : "ILLEGAL DATA VALUE",
		b"\x04" : "ILLEGAL RESPONSE LENGTH",
		b"\x05" : "ACKNOWLEDGE",
		b"\x06" : "SLAVE DEVICE BUSY",
		b"\x07" : "NEGATIVE ACKNOWLEDGE",
		b"\x08" : "MEMORY PARITY ERROR"
	}.get(code, "Wrong exception code... Probably the host is not running ModBus...")

def main():
	address= options[0][1]
	port= options[1][1]
	slave_id= options[2][1]
	register= options[3][1]
	
	if port == '' or address == '' or slave_id=='' or register=='' :
		print("[!] You should specify all parameters...")
		return None
	elif int(port) < 1 or int(port) > 65535 or not port.isnumeric():
		print("[!] You specified a wrong port...")
		return None
	elif int(slave_id) < 1 or int(slave_id) > 256 or not port.isnumeric():
		print("[!] You specified a wrong ID...")
		return None
	elif int(register) < 1 or int(register) > 65535 or not port.isnumeric():
		print("[!] You specified a wrong register...")
		return None
	
	slave_address = bytes([int(slave_id)])
	data = b"\x00\x00"+(int(register)).to_bytes(2,'big')
	
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
		s.settimeout(3)

		s.connect((address,int(port)))
		payload = transaction_identifier + protocol_identifier +lenght_field + slave_address + mod_function + data
		s.send(payload)
		response = s.recv(256)
		s.close()

		if (response[0:5] == b"\x18\x03\x00\x00\x00") and (response[7:8] == mod_function) :
			print("\n[+]VALUE: "+str(int.from_bytes(response[-2::],'big')))
		elif 	(response[0:5] == b"\x18\x03\x00\x00\x00") and (response[7:8] == exception_code) :
			print("[-] Exception thrown:")
			print(exception(response[8:9]))
		else:
			print("[-] Operation failed... Error getting ModBus response...")
	except:
		print("[-] Host is offline or you specified a wrong port...")

if __name__ == "__main__" :
	main()
