作者:綠盟科技
作者博客:http://blog.nsfocus.net/xiaomi/

近兩年,物聯網技術發展迅猛,各樣的智能設備漸漸地走進了我們的家居生活。在眾多的智能設備廠商中,小米是較早的布局智能家居生態的廠商,購買智能家居設備的用戶幾乎都會有一到兩個小米設備。那么是否可以控制這些小米設備呢,其中過程是否會有安全風險呢?本文接下來會主要介紹這些內容。

具體地,除了米家app控制小米設備外,小米還提供一種局域網控制的方式,但前提是要獲得用于設備認證一串字符串(即token),所以接下來主要介紹如何獲取設備token,以及如何實現局域網控制設備。

一、總體流程介紹

在同一局域網下,小米設備可以使用專有的加密UDP網絡協議miio協議通信控制。在網絡可達的前提下,向小米設備發送一串hello bytes即可獲得含有token的結構體數據。之后,構造相應的結構體,并以同樣的方式發送給設備即可實現控制。具體流程如下圖所示:

二、小米設備token獲取

小米設備的token獲取有三種途徑,如下所述:

2.1 miio獲取token

miio有基于Python實現的庫,其Github項目地址為:https://github.com/rytilahti/python-miio。該項目支持所有兼容miio協議的設備,并將設備發現、識別和控制的方法進行了分類。

2.1.1 環境安裝

python-miio需要Python3.5以上版本上才能運行,所以首先搭建Python環境。下面,我們在操作系統為Ubuntu的電腦或者樹莓派中安裝Python3.5:

安裝5依賴(本機存在的會忽略)

sudo apt-get install build-essential lib
sqlite3-dev sqlite3 bzip2 libbz2-dev libssl-dev openssl libgdbm-dev liblzma-dev libreadline-dev libncursesw5-dev

編譯安裝5

wgethttps://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz

tarzxvfPython-3.5.2.tgz

cd./Python-3.5.2

./configure--prefix=/usr/bin/python3.5

sudomake

sudomakeinstall

編譯后運行一下5,結果如下證明安裝成功

sean@ubuntu:~/Desktop/week/ProcessAndDeadline$ python3.5

Python3.5.2(default,Nov232017,16:37:01)

[GCC5.4.020160609]onlinux

Type"help","copyright","credits" or"license" formoreinformation.

>>>

安裝miio庫,下載庫代碼到本地并安裝

gitclonehttps://github.com/rytilahti/python-miio

cd python-miio/

python3.5 setup.py install
2.1.2 通過腳本獲取token

下面就以小米智能插座為例,說明如何獲取該設備的token。

腳本編寫

首先要保證獲取token的客戶端要與插座網絡可達。為了顯示直觀,我們將主要實現代碼從庫中提取出來(如下)。將文件放在python-miio/miio目錄下(該腳本主要就是使用socket向設備ip的54321端口發送固定字符串,返回值即為設備token):

#-*-coding:utf8-*-

import codecs

import socket

from protocol import Message

helobytes=bytes.fromhex('21310020ffffffffffffffffffffffffffffffffffffffffffffffffffffffff')

s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

s.sendto(helobytes,('192.168.42.17',54321))#插座ip,端口54321

data,addr=s.recvfrom(1024)

m=Message.parse(data)

tok=codecs.encode(m.checksum,'hex')

print(m)

print(tok)

運行:5 miio_test.py

返回如下消息結構,其中checksum即為設備的token,解碼之后為:’1d0616858062b8f836ebcacc98e62dd2’。

root@raspberrypi:~/python-miio/miio#python3.5miio_test.py

Container:

    data=Container:

        offset2=32

        offset1=32

        length=0

        value=  (total0)

        data=  (total0)

    header=Container:

        offset2=16

        offset1=0

        length=16

        value=Container:

            length=32

            unknown=0

            device_id=\x03\xa9\x84\xb6(total4)

            ts=1970-01-2222:10:05

        data=!1\x00\x00\x00\x00\x00\x03\xa9\x84\xb6\x00\x1c\xe7=(total16)

    checksum=\x1d\x06\x16\x85\x80b\xb8\xf86\xeb\xca\xcc\x98\xe6-\xd2(total16)

 b'1d0616858062b8f836ebcacc98e62dd2'

支持這種方式拿token的還有小米的空氣凈化器、凈水器、掃地機器人、智能插座插線板等。具體列表見https://github.com/rytilahti/python-miio

2.2 從米家app獲取token

如果能用上述的探測方法獲取token還是比較便捷的,但目前只有部分小米設備支持。接下來還有一種方法可以直接從app獲取token。以小米綠米網關為例:首先下載米家app,將綠米網關配置入網后,點擊網關設備。接下來步驟如下組圖,最后的密碼即為網關的token。

目前綠米的這種設計模式是用戶友好的,而且設備的所有者還可以選擇是否開放局域網控制以及刷新控制token的有效性,比較安全。個人還是很希望小米的其他設備同樣開放app側獲取設備token,因為畢竟獲取需要搭建復雜的環境以及調試代碼,大部分使用者應該不能接受的。

2.3 從數據庫獲取token

該方法是讀取手機中米家的app中的數據記錄來獲取設備的token,具體步驟如下:

  • 準備一部獲取root權限的安卓手機
  • 安裝米家app并登錄賬號
  • 進入/data/data/com.xiaomi.smarthome/databases/
  • 拷貝db,下載到電腦
  • 前往網站(http://miio2.yinhh.com/),上傳db,點擊提交,即可獲得token。

因為沒有root的安卓手機,筆者沒有具體測試這種方式獲取token的有效性,具體可以參考這篇文章(https://homekit.loli.ren/docs/show/12

三、控制小米WiFi插座

如果獲得了token,就能對小米的設備進行操作,接下來介紹使用miio協議控制小米插座的主要步驟。

3.1 控制腳本編寫

基于1.1.2獲取到的回傳的token信息,構造如下數據結構,用來控制設備。

ts=m.header.value.ts+datetime.timedelta(seconds=1)

cmd={'id':1,'method':'set_power','params':['on']}

header={'length':0,'unknown':0x00000000,                  

          'device_id':device_id,'ts':ts}

msg={'data':{'value':cmd},

       'header':{'value':header},

       'checksum':0}

其中:

token為獲取到的設備token;

device_id為獲取token返回結構中的device_id字段;

ts是一個時間結構,控制傳的ts的需要在獲取到ts基礎上加1秒;

cmd中的method包括:set_power(控制開關)、get_prop(獲取狀態),控制的params是[‘on’]/ [‘off’],獲取狀態的params是[‘power’, ‘temperature’]

下面的代碼是實現打開插座的控制,其中插座的IP為192.168.42.17。

m0=Message.build(msg,token=m.checksum)

s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

s.sendto(m0,('192.168.42.17',54321))

data,addr=s.recvfrom(1024)

m1=Message.parse(data,token=tok)

print(m1)

3.2 執行控制腳本

執行上面編寫的控制代碼,得到返回狀態,如果返回狀態中value變量中的result字段為[ ‘ok’],即為控制成功。當cmd使用get_prop方法時,如果返回的value變量值為{‘id’: 1, ‘result’: [‘on’, 59]}時,表示插座狀態是開的,溫度為59℃。因為篇幅關系具體就不貼了。

root@raspberrypi:~/python-miio/miio#python3.5miio_test.py

Container:

    data=Container:

        value={'id':1,'result':['ok']}

        data=\x03\x88T\x86\x1a\xbd\xb5\xb24.\xdcm\xdb\xc3\xb4\xdb\x0e7\x80JR\x0e\xda\xa987\x91Q\xd0\xee\x9bV(total32)

        offset2=64

        offset1=32

        length=32

    header=Container:

        value=Container:

            length=64

            unknown=0

            device_id=\x03\xa9\x84\xb6(total4)

            ts=1970-01-2220:40:38

        data=!1\x00@\x00\x00\x00\x00\x03\xa9\x84\xb6\x00\x1c\xd2F(total16)

        offset2=16

        offset1=0

        length=16

    checksum=\xfc\xe2\r\x8b\xd9\xb6\x1d\xfda\xd5\x11\x04\xe1b\xbe\xfd(total16)

四、總結

從目前的智能家居市場來看,用戶不會只使用單個智能設備廠商的設備,所以對于廠商來說,通過開放接口給用戶一些局域網的控制“自由”,實現不同廠商設備的聯動是一個不錯的選擇。

從另外一個角度,本文中體現的安全問題我們也不容忽視。如果如2.1所示在局域網中不經過認證就能獲取物聯網設備的訪問憑證,并進而進行控制,無形中給入侵者留了一扇門。例如,攻擊者可經過掃描互聯網發現家庭路由器,并利用弱口令或設備漏洞獲得路由器的shell權限,接下來就可按照文中步驟就可以獲得設備token進而控制。

在接下來的文章中,我們會給大家介紹一些智能家居的平臺,以及家庭環境中智能設備的一些安全防護方法,讓智能與安全同行。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/616/