- A+
近来我热衷于对家庭自动化设备的破解,然后将它们添加到我的Homekit集成包之中。这事情要从几个月前说起,当时我爸订购了大批量的RAEX 433MHz射频电动窗帘,以替代老式的手动式窗帘。
注意:你可以在Spotlight网店搜索Motion Motorised Roller Blind找到该窗帘。
我对这个电动窗帘非常满意,有了它我就不用跑上跑下的去打开/关闭窗户,可是为了控制它们,你需要购买RAEX的遥控器。RAEX有许多不同类型的遥控器产品,其中我选择购买了以下两种遥控器:
R 型遥控器 (YRL2016)
X 型遥控器 (YR3144)
给每间房都装上一个遥控器是不现实的,相反数个房间可以使用同一个遥控器倒还挺实在的。然而也正是因此,遥控器的局限性也就体现出来了。
另外的备选方案则是使用一块被称为RM Pro的硬件,通过使用他们的应用程序进行远程控制。
于我而言该App卡顿,bug又多,不太适合用于家庭自动化生态系统。我希望电动窗帘可以通过Apple Homekit进行行为。
为了控制这些窗帘,我知道我需要达成以下条件中的一个:
- 逆向App中RM Pro与转子直接的通信协议
- 逆向遥控器中用于与窗帘通信的射频协议
尝试第一个条件,发现无法拦截iPhone与集线器之间的通信流量。所以给我的选择只有逆向射频协议咯。
在Ebay上购置了一对433MHz的Arduino发射器/接收器。如果链接失效你可以在Ebay上搜索433Mhz RF transmitter receiver link kit
初步研究
通过谷歌搜索,没有获取到许多RAEX所使用协议的相关技术说明的结果:
- 通过FCC或专利查询未能找到协议的技术说明
- 向RM Pro官方发送电子邮件寻求技术说明文档,然而他们没能理解我的意思
- 向RAEX官方发送电子邮件寻求技术说明文档,回应说他们因为保密协议不会提供给我
- 我发现RFXTRX能够通过BlindsT4模式控制窗帘,该模式也适用于Outlook智能窗帘。
- 打开其中一个遥控器之后辨识其使用的微控制器,然而无法找到确定其用于解析的通用射频编码方案。
- 似乎可以通过遥控器上的I2C总线转储ROM芯片对该固件进行逆向分析
- 这与允许遥控器在引导后的任何时候进行转储有异曲同工之妙
数据捕获
当数据包到达连接到Arduino的接收器,之后开始搜寻用于捕获传输数据的Arduino sketches设计。虽然尝试多次都失败了,但功夫不负有心人最终找到了一个可能用的
确定理解这些知识点(1,2,3)然后看了一些射频逆向分析的相关文章。其中大部分的想法都是通过接收器插入计算机的麦克风端口,使用Audacity来抓取信号。这个思路很不错,就这么搞!
捕获到大量数据,其中有4种R型号遥控器数据,以及2种X型号遥控器数据。更有趣的是8种不同设备都与Broadlink RM Pro(B型号)配对成功。
至此,我确定了一些事情:
- 传输过程没有滚动码进行身份验证,因此可以将捕获的信号重放并使智能窗帘每次都重复该行为。如果无法逆向该协议,这将是最坏的情况。
- 传输至少重复3次(根据所使用的遥控器类型而有所不同)
缩放波形,我们可以看出所捕获信息的不同之处。以下例子是捕获到的配对行为:
放大:
在缩放图像中,您可以看到传输以振荡的0101 AGC模式开始,随后是另一个双宽度报头模式,再然后是一个更长的数据头模式,最后则是传输的数据。对于R型遥控器,报头,数据头及数据将重复3次(AGC模式仅在传输开始时发送一次),这可以在第一个图像中看到。
仅观察波形数据对我们帮助不会太多,我们将其数字化以分析其二进制,然后确定不同的遥控器,信道以及行为之间的关系。
波形解码
我们需要确定波形是如何进行编码的。这类硬件应用通常都使用以下编码方式:
- 曼彻斯特编码
- 三态/三位(Tri-State/Tri-bit)编码(参考信息)
- PWM编码
- RAW? high long = 11,high short = 1,low long = 00,low short = 0
参考一些资料后,确定其使用的编码可能是曼彻斯特编码。
将数据数字化
一开始我是以上面提到的RAW方案处理数据(即使我确认是曼彻斯特编码)。这是因为如果碰巧不是曼彻斯特编码,我可以用另一种方案来尝试解码。
我将每个捕获数据都写入Google电子表格。将每个信道中对应的行为写入表格大概就花费了5分钟,且一个遥控器就有6个信道。
当我搜集完2个遥控器的8个不同信道的所有行为就停了下来。这一下就拿到了32个捕捉数据,从这些数据推断出RAW bits相关信息:
- 每个信道的位有些许变化
- 每个遥控器的位有些许变化
- 信道/遥控器/行为组合,有些位似乎会进行随机性变化(这是某种校验和?)
此外我需要一个脚本来处理我通过Audacity捕获的WAV文件。我写了一个脚本,用于检测数据头并提取等效于RAW编码的数据(之前都是手工完成)。该脚本以JSON格式进行输出,因此我可以添加额外的metadata,并用波形再次确认捕获的数据:
[
{
"filename": "/Users/nickw/Dropbox/RF_Blinds/Export_Audio2/tracks2/R1_CH1.wav",
"captures": [
{
"data": "01100101100110011001100101101001011010010110011010011010101010101010101010011001101010101010101010101010101",
"header_pos": 15751,
"preamble_pos": 15071
},
{
"data": "01100101100110011001100101101001011010010110011010100110101010101001101010011001101010101010101010101010101",
"header_pos": 46307,
"preamble_pos": 45628
},
{
"data": "01100101100110011001100101101001011010010110011010010110101010101010011010011001101010101010101010101010101",
"header_pos": 73514,
"preamble_pos": 72836
},
{
"data": "01100101100110011001100101101001011010010110011010101010101010100101010101101001011010101010101010101010101",
"header_pos": 103575,
"preamble_pos": 102895
}
]
}
]
验证完成后,将这些数据进行列表并将其插入电子表格进行后续处理。数据太多,不得不小心应对:
如果以曼彻斯特编码进行解密那就最好了。为此我又写了一个脚本,将捕获到的RAW数据转换成曼彻斯特编码(或其他类型编码)。将这些数据上传到电子表格中,之后进行一系列分析
从这些数据中,可以立马发现二进制位与其目的之间的一些联系:
- 信道(C)有6 bits
- 行为(A)有2 bits
- 用于校验和的6 bits,似乎存在一个行为与信道之间的函数关系F(A, C)
- 当行为改变时会变化
- 当信道改变时会变化
- 由于没有等效的信道,所以不能确定它们在遥控器中会发生改变
- 1 bit似乎是与行为相关的函数F(A)
- 1 bit似乎与函数F(A)相关,即G(F(A))。其取决于F(A)值,有时是1对1映射,有时是逆映射
经过进一步的检测,我确定对于同一个遥控器和信道,每个不同的行为,F(A, C)函数的值将增加1(考虑高位优先)
同时对于相邻信道,与C(信道)相关的位向上/向后计数(X型遥控器向上计数,R型遥控器向后计数)。注意C列,此外F(C)还会同时增加/减少。
至此我确定了F(A, C)与信道C之间的关系,例如F(A, C) = F(PAIR, C0) == F(PAIR, C1) ± 1。有了这个发现之后,我还确定了F(A, C)和行为A之间的数学关系。
获取更多数据
从我们现在已经获取到信息来看,似乎可以通过改变6位信道数据以及对应的校验和,然后遵循上面发现的数学关系来创建一个新的遥控器。即我们可以从一个子母信道生成64个信道,这么多信道已经足以控制房子里所有的智能窗帘。但是我真的想完全解码校验和字段,以此生成一个几乎无上限信道的遥控器。
我写了一个工具输出捕获到的字幕信道:
./remote-gen generate 01000110110100100001010110111111111010101
...
我推断后面生成的这些数据可以帮助我们确认校验和的形成,当然这得我们在同一信道上去观察不同的遥控器信息,即R0CH0,R1CH0,X1CH0,等…
从本质上来讲我们要解决的就是有关函数G的方程式:
F(ACTION_PAIR, CH0) == G(F(ACTION_PAIR, CH0))
然而,查看所有信道0的配对行为捕获,校验和似乎依旧混乱/随机:
观察数据的同时,另一种模式出现在我们视野中。G(F(A))与F(A)隔了一个完整的字节偏移量(8 bits)F(A)。此外F(A, C)的前2 bits位于字节边界且与行为A对齐。随着行动A的增加,F(A, C)也增加。
我们需要确认一些基于前4个字节产生已知校验和的函数。在开头我尝试在字节上进行XOR操作:
不太成功,输出似乎是随机的,并且使用校验和进行XOR输出没有产生常数键。因此,我推断校验和不是通过XOR生成的。会不会是数学上的加法呢?我们已经看到前文提到的加法/减法关系。
对于同类型遥控器的不同信道,其中存在一个定量差异。或许是程序有BUG,所以不同类型的遥控器直接那个定值有所不同吗?在校验和或者信道改变时,我们或许没有正确封装bits数量或使用了错误的字节边界?
解决校验和
查看原始捕获信息,并执行相同的模增加,我们确定校验和是通过4个主要字节和按模增加3来计算的。不同于RAEX想要使其解码的校验和更加困难或者说是确保一个恰当的传输模式,我不知道这里为什么要增加3
我重构了应用程序以处理刚刚识别的边界:
type RemoteCode struct {
LeadingBit uint // Single bit
Channel uint8
Remote uint16
Action uint8
Checksum uint8
}
事实证明,F(A)不是与行动A相关的函数,它实际上是被传输的行为数据的一部分:
type BlindAction struct {
Name string
Value uint8
}
var validActions = []BlindAction{
BlindAction{Value: 127, Name: "PAIR"},
BlindAction{Value: 252, Name: "DOWN"},
BlindAction{Value: 253, Name: "STOP"},
BlindAction{Value: 254, Name: "UP"},
}
此外,信道和遥控器之间的联系也许并没有那么重要。它可能就是一个任意的24位整数,不管怎样它更容易被拆分为一个8位整型数据和一个16位整型数据。总算可以写校验和函数了:
func (r *RemoteCode) GuessChecksum() uint8 {
return r.Channel + r.Remote.GetHigh() + r.Remote.GetLow() + r.Action.Value + 3
}
附加工具
remote-gen程序本就是用于生成子母遥控器代码(尽管出现了封装问题),我们现在再添加一些其他功能,让其更强大。
我需要一种从捕获数据中提取信息,然后验证其校验和是否与我们生成的校验和规则集一致:
./remote-gen info 00010001110001001101010111011111101010100 --validate
Channel: 196
Remote: 54673
Action: STOP
Checksum: 42
Guessed Checksum: 42
如果两个校验和不相同,则运行–validate就会出现错误。添加的另一个功能则是能够生成任意代码以创建属于我们自己的遥控器:
./remote-gen create --channel=196 --remote=54654 --verbose
00010001101111110101010111111111010011001 Action: PAIR
00010001101111110101010110011111101101000 Action: DOWN
00010001101111110101010111011111111101000 Action: STOP
00010001101111110101010110111111100011000 Action: UP
你可以通过nickw444/homekit/blindkit查看项目所有的资源
全文终!
*参考来源:nickwhyte,freebuf小编鸢尾编译