Home | 簡體中文 | 繁體中文 | 雜文 | 打賞(Donations) | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 知乎專欄 | 視頻教程 | About

2.5. Mototrbo 數字電台短信協議分析

2.5.1. IP Address 與 Radio ID 相互轉換

DMR 網絡

Mototrbo DMR 實際上就是 TCP/IP網絡的延伸。我們可以理解為就是一個TCP/IP網絡。

首先Radio ID (八位數字)就是 IP地址的後三位,色碼就是IP地址的第一位。例如 12 色碼對應IP地址就是 12.0.0.0

色碼加上RadioID通過算法就能實現 Radio ID 與 IP 地址的相互轉換,組呼可以理解為通過子網掩碼控制廣播域,在相同子網下的IP地址可以接收組播信息。

代碼如下:

		
class Protocol():
	def __init__(self, buffer = None):
		self.buffer = buffer
	def header(self):
	   return(self.message)
	def message(self):
		return(self.message)
	def id2ip(self, cai, id):
		return (str(cai)+"."+str((id >> 16) & 0xff) +'.'+ str((id >> 8) & 0xff) + '.' + str(id & 0xff));
	def ip2id(self, ipaddr):
		a, b, c, d = ipaddr.split('.');
		return ((int(b) << 16) + (int(c) << 8 ) + int(d))		
		
			

id2ip 是Radio ID 轉 IP 地址,算法如下

		
(str(cai)+"."+str((id >> 16) & 0xff) +'.'+ str((id >> 8) & 0xff) + '.' + str(id & 0xff))
		
			

ip2id 是 IP 地址 轉 Radio ID

		
a, b, c, d = ipaddr.split('.')
((int(b) << 16) + (int(c) << 8 ) + int(d))	
		
			

2.5.2. 檢查 Radio 狀態

判斷電台是否開機

數字電台使用的多了,就不想喊CQ了,直接進入通信錄,找到朋友,檢查狀態。如果對方在綫就會出現一個綠色的"對號",同時對方也會振鈴。

其實我們判斷電台是否開機很簡單。每個電台都是一計算機終端,色碼 + Radio ID 就能算出對方的IP 地址,然後直接 ping 對方的IP地址就可以了。

			
工具程序地址: https://github.com/netkiller/Mototrbo/blob/master/Mototrbo.py

#直接 ping IP 地址             
os.system("ping %s" % self.options.ping) 

# ping radio id 需要做一次轉換            
os.system("ping %s" % protocol.id2ip(int(self.options.cai), int(self.options.online)))
		
		
			

2.5.3. TMS 短信協議分析

頭部:0x00開始,然後是短信內容的長度,0x0e 0x00 分割,然後是序號,最後是 0x04 結尾 內容:\r回車符,0x00,換行符\n,信息內容,0x00結束,短信的字符集是 utf-16

一條完整短信最終協議包如下:

			
b'\x00\x14\xe0\x00\x88\x04\r\x00\n\x00B\x00G\x007\x00N\x00Y\x00T\x00'			
			
			

上面短信的內容就是 BG7NYT

短信發送

			
class TMS(Protocol):
	def __init__(self, buffer = None):
		super().__init__(buffer)
		self.port = 4007
		if buffer :
			self.header = self.buffer[:6]
			self.message = self.buffer[6:]
	def encode(self, msg):
		return(msg.encode('utf-16').replace(b'\xff\xfe', b'\x00'))

	def decode(self, msg = None):
		if msg :
			return( msg.replace(b'\x00', b'\xff\xfe', 1).decode('utf-16') )
		else:
			return( self.message.decode('utf-16') )
	def sequence(self):
		return(self.header[4:5])
	def lenght(self):
		return (len(self.message))
	def sendtoip(self, ipaddr, sms):
		import socket
		sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
		message = self.encode(sms)
		length = len(message)+7
		#print(hex(length))
		protocol = b'\x00'+ bytes([length])+ b'\xe0\x00\x88\x04\r\x00\n' + message
		#print(protocol)		
		sock.sendto(protocol, (ipaddr, self.port))
	def sendtoid(self, cai, radioid, sms):
		ipaddr = self.id2ip(cai, radioid)
		self.sendtoip(ipaddr, sms)
	def debug(self):
		protocol = b'\x00\x14\xe0\x00\x88\x04\r\x00\n\x00B\x00G\x007\x00N\x00Y\x00T\x00'
		tms = TMS(protocol)
		
		#print(tms.id2ip(7558888))
		#print(tms.ip2id('12.115.86.232'))
		#print(tms.ip2id('12.115.86.12'))
		#print(tms.ip2id('12.115.52.56'))
		#print(tms.ip2id('192.168.11.1'))
		#print(tms.id2ip(11012865))
		print(tms.message)
		
		print(tms.lenght())
		
		print(tms.encode('BG7NYT'))
		print(tms.decode(tms.encode('BG7NYT')))
		
		tmp = TMS(b'\x00\x16\xe0\x00\x83\x04\r\x00\n\x00B\x00e\x00l\x00i\x00e\x00v\x00e\x00')
		print(tmp.decode())
		print('= Send')

		#.replace(b'\x00', b'')
		#header = protocol[:6]
		#message = protocol[9:]
		#print(tms.decode(message))				
			
			

編碼和解碼

因為我們目前計算機通常使用UTF-8字符集,所以短信需要編碼為 UTF-16 才能發送,否則在終端上看到的是亂碼。 同理計算機收到來自電台的短信也需要解碼 UTF-16才能閲讀。代碼如下

			
	def encode(self, msg):
		return(msg.encode('utf-16').replace(b'\xff\xfe', b'\x00'))

	def decode(self, msg = None):
		if msg :
			return( msg.replace(b'\x00', b'\xff\xfe', 1).decode('utf-16') )
		else:
			return( self.message.decode('utf-16') )			
			
			

短信的發送

發送短信很簡單,通過色碼 + Radio ID 計算出對方的IP地址。UDP協議與對方IP地址建立連接,然後發送,代碼如下:

			
	def sendtoip(self, ipaddr, sms):
		import socket
		sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
		message = self.encode(sms)
		length = len(message)+7
		#print(hex(length))
		protocol = b'\x00'+ bytes([length])+ b'\xe0\x00\x88\x04\r\x00\n' + message
		#print(protocol)		
		sock.sendto(protocol, (ipaddr, self.port))			
			
			

程序運行後對方的電台就會收到你的短信。

到此為止,我也曾經嘗試分析 ARS,LRRP ......等等協議,很想實現ARS將GPS坐標抓出來,放到地圖上,實現aprs.is那樣的功能。 逆向工程太複雜,放棄了。

相關軟件:https://github.com/netkiller/Mototrbo