本文以打開無線控制的電動車庫卷簾門為目標(biāo)靶壮,深入研究了ASK/OOK的編/解碼,并用樹莓派+五元錢的發(fā)射模塊實現(xiàn)了打開車庫門的各種姿勢。本文適用于主流315/433MHz射頻遙控扩借。
背景
車庫裝了電動卷簾門,為了了解其安全性缤至,也是為了能自主控制潮罪,研究了下其遙控原理康谆。其實在這個過程中,我測試了家里幾乎所有的無電線遙控器嫉到,包括電動窗簾沃暗、投影幕布、電動衣架何恶、車鑰匙孽锥。除了車鑰匙,其它都是類似的细层,即ASK/OOK編碼惜辑。
ASK,簡單的理解疫赎,就是調(diào)幅盛撑,用不同的幅度來代表不同的信息。OOK是ASK的特例捧搞,因為只有0和1要表示抵卫,可以用有載波來代表1,無載波來代表0胎撇。但實際上并不是這么直接介粘,通常會加上脈寬調(diào)制(PWM)以提高抗干擾能力。
用HackRF確定可行性
據(jù)說有的車庫門是滾動碼的(編碼是變化的)创坞,我們可以先用HackRF做個簡單的重放攻擊測試碗短。
錄制2秒的信號并重放:
hackrf_transfer -f 433920000 -s 2000000 -a 1 -r capture.raw -n 4000000 -g 40 -l 16
hackrf_transfer -f 433920000 -s 2000000 -a 1 -t capture.raw -x 40
部分運行提示:
call hackrf_set_sample_rate(2000000 Hz/2.000 MHz)
call hackrf_set_hw_sync_mode(0)
call hackrf_set_freq(433920000 Hz/433.920 MHz)
call hackrf_set_amp_enable(1)
samples_to_xfer 4000000/4Mio
Stop with Ctrl-C
3.9 MiB / 1.000 sec = 3.9 MiB/second
3.9 MiB / 1.000 sec = 3.9 MiB/second
0.3 MiB / 1.000 sec = 0.3 MiB/second
Exiting... hackrf_is_streaming() result: streaming terminated (-1004)
實測用錄制的信號是可以控制的(如果不行,注意調(diào)整HackRF放大器的增益)题涨。但這個沒多大技術(shù)含量偎谁,而且成本高,數(shù)據(jù)量也大纲堵。我們的目標(biāo)是解碼后再重新編碼/重放巡雨。
用GNU Radio錄制信號
用GNU Radio搭一個簡單的接收框圖,一方面將接收信號保存到文件席函,另一方面將信號以瀑布圖顯示作為實時反饋铐望。因為遙控信號是433.92MHz,中心頻率設(shè)在這個附近都可以茂附;采樣率2M就夠了正蛙。
下圖是運行時的瀑布圖,其中按了5次遙控器营曼。
用 Inspectrum 手動解碼
用apt-get安裝inspectrum乒验,或下載最新的Inspectrum代碼,按照文檔自行編譯蒂阱。試過Debian和Mac下都沒問題(Mac下用MacPorts要安裝一堆依賴)锻全。編譯就不多說了狂塘,下面是解碼的主要步驟:
- 用Inspectrum打開前面錄制的文件,設(shè)置采樣率為錄制時的采樣率(2M)鳄厌;
- 水平拖動荞胡,找到有信號的區(qū)域;
- 在原始信號上右鍵了嚎,Add derived plot => Add sample plot泪漂;
- 此時原始信號上會出現(xiàn)兩條水平線,用鼠標(biāo)拖動新思,調(diào)節(jié)中心頻率的位置和寬度窖梁;
- 在原始信號上右鍵,Add derived plot => Add amplitude plot夹囚;
- 在Amplitude plot上右鍵纵刘,Add derived plot => Add threshold plot;
- 勾選"Enable cursors"荸哟,此時會出現(xiàn)兩條豎線假哎;
- Zoom放大信號圖,移動兩條豎線鞍历,使其寬度包含一個符號舵抹。注意跳過前導(dǎo)的高低電平(start1, start0)。數(shù)據(jù)通常是脈寬編碼的劣砍,一對高低電平的組合代表一個bit:高電平較寬的代表1惧蛹,低電平較寬的代表0。從圖中應(yīng)該能看出這個規(guī)律刑枝。
- 改變符號數(shù)香嗓,使其包含整個信號區(qū)域(圖中是65個符號,這相當(dāng)于完整的key)装畅,并調(diào)節(jié)首尾對齊(結(jié)束時通常有較長的低電平)靠娱,這時可以得到符號的速率,即波特率(對OOK掠兄,其實等同于比特率)像云。
最后,在Amplitude plot或Threshold plot上分別點右鍵蚂夕,Extract symbols (to stdout)迅诬,可以得到解碼的數(shù)據(jù)。其中前者相當(dāng)于模擬信號婿牍,簡單理解:正數(shù)代表1侈贷,負(fù)數(shù)代表0;后者才是我們想要的bit流牍汹。
為確認(rèn)解碼正確铐维,可以再選一段信號區(qū)域,做同樣的操作慎菲,看結(jié)果是否一致嫁蛇。畢竟ASK抗干擾不強,有時候可能會差一兩個bit露该。通常睬棚,按一下遙控器,同樣的數(shù)據(jù)會重復(fù)發(fā)送幾次解幼。
遙控信號編碼分析
根據(jù)前面的解碼抑党,以及對更多遙控器的分析,可以歸納出一個模型撵摆。一個ASK信號包含如下部分(參數(shù)):
- start1: 起始的高電平時間長度底靠;
- start0: 起始的低電平時間長度;
- stop0: 結(jié)束的低電平時間長度特铝;
- period: 每個bit的周期暑中,在PWM編碼中,每個bit都對應(yīng)一對高/低電平鲫剿,而且總是先高后低鳄逾;
- duty: 占空比,比如占空比是75%灵莲,則意味著一個周期內(nèi)如果75%左右是高電平雕凹,則代表1; 而75%左右是低電平則代表0;
- bits: 實際的bit流政冻。
這里的占空比肯定是大于50%的枚抵,通常在75%左右比較合適,這樣既能拉開(每個周期內(nèi))兩種電平的比例差赠幕,減少接收端的誤判俄精;又能保證接收時能采樣到兩種電平,也是為了減少誤碼榕堰。試想對于99%的占空比竖慧,1%周期的電平很可能被接收端采樣不到,導(dǎo)致采樣到199%(甚至更長)周期的同一種電平逆屡,這樣解碼時就會出錯圾旨。
發(fā)射模塊
最初,我是想用GNU Radio做ASK/OOK編碼并發(fā)射的魏蔗。萬能的HackRF和SDR按說能搞定這個小Case砍的。
研究了下,發(fā)現(xiàn)這并不是一件容易的事莺治。需要使用很多的模塊廓鞠。這也許是一個很好的GNU Radio的練習(xí)題帚稠。但我還是先看下有沒有更簡單的辦法。
然后口水了一下TI的EZ430-Chronos手表床佳,找了下“廉價”的RFcat滋早,發(fā)現(xiàn)并不容易買到。最后在萬能的假貨寶發(fā)現(xiàn)了真正廉價的東東:只要5元F雒恰(你買不了吃虧杆麸,買不了上當(dāng)。浪感。)
這個模塊很簡單昔头,就是把輸入的信號以433/315M的載波調(diào)制/發(fā)射出去。DATA為高電平影兽,就按高電平調(diào)幅輸出(請注意揭斧,這里調(diào)制的是電平,并不是數(shù)據(jù)赢笨。也就是說未蝌,數(shù)據(jù)"1"對應(yīng)多長時間的高電平,多長時間的低電平茧妒,這個模塊都不管的——這些是編碼模塊要處理的事)萧吠。
用Python編碼
為了代碼的模塊化,也是為了減少發(fā)射時的計算量桐筏,我們采取先編碼再發(fā)送的方案纸型。根據(jù)前面建立的ASK信號的模型,將這個信號編碼為高低電平交替的波形梅忌,并用一個數(shù)組表示狰腌,數(shù)組中每個元素存儲高低電平切換時對應(yīng)的時間戳。波形總是以高電平開始牧氮。
起始/結(jié)束電平的時長琼腔、占空比這些參數(shù)理論上并不需要嚴(yán)格準(zhǔn)確,但這取決于接收端的寬容度踱葛,所以我們還是盡量忠實于原信號丹莲。
下面是核心的代碼片斷,其中ts是時間戳數(shù)組尸诽。
def encodePWM(self, ts):
t = 0
ts.append(t)
t += self.start1
ts.append(t)
t += self.start0
ts.append(t)
for i in range(0, self.bits.len):
w = self.duty if self.bits[i] else 1 - self.duty
ts.append(t + self.period * w)
t += self.period
ts.append(t)
ts[-1] += self.stop0
用樹莓派發(fā)送
發(fā)送工作就很簡單了:將發(fā)射模塊的DATA腳與樹莓派的某個GPIO相連甥材,電源也直接用樹莓派的;
然后根據(jù)時間戳交替翻轉(zhuǎn)對應(yīng)的GPIO就行了性含。
def send(self, ts):
b = 1
t1 = time.time()
GPIO.output(self.pin_tx, b)
t1 -= ts[0]
for t in ts[1:-1]:
b = 1 - b
wait = t1 + t - time.time()
if wait > 0:
time.sleep(wait)
GPIO.output(self.pin_tx, b)
wait = t1 + ts[-1] - time.time()
if wait > 0:
time.sleep(wait)
用sleep控制時間盡管有一定誤差洲赵,腳本語言的運行也沒那么快,但實測是夠用的。下圖是示波器上看到的DATA引腳的波形圖(兩個通道都連著DATA腳)叠萍。
為便于觀察芝发,我將編碼周期設(shè)置為1ms,和示波器界面的1ms/div對應(yīng)苛谷。圖中測量的間距是2.78ms(預(yù)期是2.75ms)后德,偏差是可接受的。
多種姿勢打開車庫門
把發(fā)射的裝置放在車庫內(nèi)抄腔,并連上網(wǎng)絡(luò),就可以無需鑰匙自主控制車庫門的開/關(guān)了理张。
手機開關(guān)門
不需要自己寫App赫蛇,用ssh終端密鑰登錄并執(zhí)行命令,就可以手機一鍵開/關(guān)門了雾叭,并且可以遠(yuǎn)程控制悟耘。
自動開關(guān)門
以指定手機作為鑰匙,當(dāng)持手機靠近車庫時(其實是連上車庫WiFi后)织狐,就自動開門暂幼。大致流程是:
- 遠(yuǎn)程執(zhí)行路由器的 iwinfo 命令(如下)檢測連接在上面的設(shè)備;
- 如果作為鑰匙的手機的MAC在列表里移迫,并且信號強度(SNR)超過設(shè)定值旺嬉,就計為一個有效的連接。當(dāng)連接數(shù)從0變?yōu)榉?時厨埋,就自動開門邪媳。
- 如果鑰匙手機的有效連接數(shù)降到0, 就自動關(guān)門。
ssh root@192.168.12.34 'iwinfo ra0 assoclist && iwinfo rai0 assoclist'
自動關(guān)門的好處是可以防止人走了忘了關(guān)門(俺家真的發(fā)生過)荡陷。
芝麻開門
理論上可以做到雨效,但需要可靠的聲紋識別。這個就算了废赞。徽龟。
鎖死車庫門
把發(fā)射模塊對應(yīng)的GPIO設(shè)為高電平,由于發(fā)射模塊信號強唉地,距離近据悔,接收端收到的總是1,導(dǎo)致用真的鑰匙也開不了門渣蜗。
結(jié)語
不用滾動碼編碼的車庫門其實是毫無安全性可言的屠尊。不管是簡單的原始信號重放、還是解碼后再編碼的重放都比較容易實現(xiàn)耕拷。但我們可以利用這種不安全為自己提供便利讼昆,更靈活地自主開/關(guān)門。另外,用發(fā)射模塊發(fā)射高電平可以干擾鑰匙的信號達(dá)到鎖死車庫門的效果浸赫。
但如果不是通過監(jiān)聽鑰匙的信號闰围,用暴力破解Key也并不是那么容易的。因為ASK編碼除了需要數(shù)據(jù)吻合既峡,載波頻率相同羡榴,還需要數(shù)據(jù)編碼速率,甚至起止電平的時長都要一致运敢。
用廉價的硬件發(fā)射模塊配合樹莓派(或單片機)可以低成本地編碼/發(fā)射ASK/OOK信號校仑,簡單易行。而HackRF加Inspectrum解碼僅適合實驗和調(diào)試用传惠,實用價值不高迄沫。后續(xù)將會嘗試ASK/OOK的自動解碼。