Mototrbo TMS 协议分析(数字电台短信协议分析)

Mototrbo TMS 协议分析

作者:BG7NYT

三年前我就分析出了Mototrbo TMS协议,一直动笔写这篇文章,最近比较闲,就想起了这事。

起初只是好玩,使用P8668发送短信输入太麻烦,我就想开发个软件从电脑发送短信跟 Mototrbo 终端。

另外当时微信很火,微信推出了菜单服务。例如关注微信公众号后出现:当时微信很火,微信推出了菜单服务。例如关注微信公众号后出现:

  1. 主菜单
  2. XXX服务
  3. XXX

通过输出数字跟微信交互。我就相处了Mototrbo 的短信服务也能这么干。可以实现订阅天气预报,发布公告信息等等.....

还有一个原因是当时摩托罗拉DMR无法跟海能达电台互通短信。于是我就好奇,分析除了TMS短信协议,当时很想把这个分享给海能达,可惜我不认识海能达的人。

回到主题

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))

判断电台是否开机

数字电台使用的多了,就不想喊CQ了,直接进入通信录,找到朋友,检查状态。如果对方在线就会出现一个绿色的"对号",同时对方也会振铃。

其实我们判断电台是否开机很简单。每个电台都是一计算机终端,色码 + Radio ID 就能算出对方的IP 地址。

直接 ping 对方的IP地址就可以了,呵呵。

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

        elif self.options.ping :            #直接 ping IP 地址             os.system("ping %s" % self.options.ping) 
        elif self.options.online :            # ping radio id 需要做一次转换            os.system("ping %s" % protocol.id2ip(int(self.options.cai), int(self.options.online)))

Mototrbo TMS 协议

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)

编码和解码

因为我们目前计算机通常使用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') )

短信的发送

发送短信很简单

  1. 通过 色码 + Radio ID 计算出对方的IP地址。
  2. 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

原文发布于微信公众号 - Netkiller(netkiller-ebook)

原文发表时间:2017-06-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏移动开发之家

GSYVideoPlayer项目说明

项目目前UI层大部分方法和变量都是protect,虽然就封装性而言这并不是很好,但你可以继承后快捷实现你的自定义。

503
来自专栏腾讯Bugly的专栏

Android 内存优化总结&实践

导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存。然而大部...

3857
来自专栏Android先生

Android Binder面试详解

目前linux支持的IPC包括传统的管道、System V IPC、即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Serve...

581
来自专栏从零开始学自动化测试

Selenium2+python自动化71-多个浏览器之间的切换

前言 有时候一些业务的功能涉及到多个系统,需要在web系统1打开造一些数据,然后用到某些参数是动态生成的,需要调用web系统2里面的参数。 举个简单例子:在做...

2916
来自专栏余果的专栏

Google分析language垃圾信息

最近一段时间,我在Google Analytics(以下简称GA)中查看网站数据时,发现一个非常可疑的信息...

7201
来自专栏程序员的SOD蜜

彻底关闭Excle进程的几个方法

之前研究过的问题,最近有朋友问,这里再总结下做一个笔记。 我们在应用程序里面通过创建Excle应用对象打开Excle的情况下,如果不注意几个问题,可能无法彻底关...

2924
来自专栏SAP最佳业务实践

SAP最佳业务实践:FI–应付账款(158)-3 F110使用付款程序过帐预付款

4.3 F110使用付款程序过帐预付款 对供应商的预付定金可使用付款程序自动过帐。 必须过帐预付款。 必须不激活支付发布清单和直联支付(EPIC)应用程序才能...

31511
来自专栏微信小程序开发

微信小程序开发常见问题(七)

知晓程序员,专注微信小程序开发的程序员! 一、判断小程序版本号 小程序的API是不断更新的,你可能使用某个API时,文档里会说明,此API在1.x.x版本开始...

4195
来自专栏企鹅号快讯

史上最全的web前端学习教程汇总!

第一阶段:HTML+CSS HTML进阶、CSS进阶、div+css布局、HTML+css整站开发、 JavaScript基础:js基础教程、js内置对象常用方...

2055
来自专栏Crossin的编程教室

Python 爬虫爬取美剧网站

一直有爱看美剧的习惯,一方面锻炼一下英语听力,一方面打发一下时间。之前是能在视频网站上面在线看的,可是自从广电总局的限制令之后,进口的美剧英剧等貌似就不在像以前...

3277

扫码关注云+社区