Для этого проекта я провел реверс-инжиниринг приложения «C by GE» для управления интеллектуальными лампочками GE, подключенными к Wi-Fi. Для этого я начал с декомпиляции приложения Android, а затем перепроектировал двоичный протокол, который приложение использует для связи с сервером. Дополнительные сведения см. в разделе Реверс-инжиниринг C от GE.
Конечными продуктами этого проекта являются:
Отказ от ответственности: этот код является результатом обратного проектирования и не соответствует спецификации протокола. В результате нет никакой гарантии, что он продолжит работать или будет работать для каждой сети или интеллектуального устройства. Хотя другие успешно использовали этот API в некоторых случаях, возможно, что код делает неверные предположения, которые не соответствуют каждому варианту использования.
Каталог сервера — это автономное веб-приложение и конечная точка API JSON для C от GE Lightbulbs. Сайт выглядит следующим образом:
Если вы запустите веб-сайт с аргументами -email
и -password
, то при первой загрузке веб-сайт откроет страницу двухфакторной аутентификации. Вы нажмете кнопку и введете код подтверждения, отправленный на вашу электронную почту. Альтернативно, вы можете войти в систему заранее, выполнив команду login_2fa с флагами -email
и -password
установленными для информации вашей учетной записи. Команда запросит у вас код подтверждения 2FA. Как только вы введете этот код, команда выдаст информацию о сеансе в виде большого двоичного объекта JSON. Затем вы можете передать этот JSON в аргумент -sessinfo
сервера, например, как -sessinfo 'JSON HERE'
. Обратите внимание, что часть сеанса истекает через неделю, но работающий экземпляр сервера продолжит работать по истечении этого времени, поскольку истекающая часть сеанса используется только один раз для перечисления устройств.
Новые учетные записи требуют использования двухфакторной аутентификации. Вы можете выполнить рукопожатие 2FA для создания сеанса следующим образом:
callback , err := cbyge . Login2FA ( "my_email" , "my_password" , "" )
// Handle error...
sessionInfo , err := callback ( "2FA code from email" )
// Handle error...
session , err := cbyge . NewController ( sessionInfo , 0 )
// Handle error...
Для более старых учетных записей, которые никогда раньше не использовали 2FA, вы можете войти в систему напрямую:
session , err := cbyge . NewControllerLogin ( "my_email" , "my_password" )
// Handle error...
После создания сеанса вы можете перечислить устройства следующим образом:
devs , err := session . Devices ()
// Handle error...
for _ , x := range devs {
fmt . Println ( x . Name ())
}
Управлять лампочками можно так:
x := devs [ 0 ]
session . SetDeviceStatus ( x , true ) // turn on
session . SetDeviceLum ( x , 50 ) // set brightness
session . SetDeviceCT ( x , 100 ) // set color tone (100=blue, 0=orange)
Вы также можете запросить текущие настройки лампы:
status , err := session . DeviceStatus ( x )
// Handle error...
fmt . Println ( status . IsOn )
fmt . Println ( status . ColorTone )
В этом разделе я расскажу вам, как я реконструировал части протокола C by GE.
Первым шагом была дизассемблирование приложения Android с помощью Apktool. Это приведет к дизассемблированию приложения Smali. Копаясь, я искал URL-адреса и доменные имена. Изначально я нашел это:
.field public static final API_VERSION : L java/lang/String ; = " v2/ "
.field public static final BASE_URL : L java/lang/String ; = " https://api-ge.xlink.cn:443/ "
Посмотрев, где использовалась эта конечная точка API, я быстро привел меня к набору HTTP-вызовов на основе JSON для входа в систему, получения списка устройств и т. д. Однако эта конечная точка, похоже, не предоставляла возможности 1) получить статус устройств или 2) обновить цвет или яркость устройств.
Должен был быть какой-то другой способ связи приложения с умными лампочками. Однако дизассемблирование было пронизано кодом для связи Bluetooth и локальной сети, и я немного волновался, что нет глобальной конечной точки API для управления лампочками. Что еще хуже, приложение C by GE жаловалось всякий раз, когда я выключал Bluetooth, а затем пытался его использовать. Однако в конце концов я обнаружил, что могу открыть приложение, позволить ему делать свое дело, а затем отключить Bluetooth и Wi-Fi, сохраняя при этом контроль над лампочками. Все, что мне нужно было сделать, это нажать кнопку «Назад» Android всякий раз, когда приложение открывало всплывающее окно с просьбой «Включить отслеживание местоположения» (заметьте, странное название для Bluetooth и Wi-Fi).
На этом этапе я был почти уверен, что приложение не устанавливает каких-либо других загадочных HTTP(S)-соединений. Однако интересно, что я нашел домен «xlink.cn» где-то в коде Smali:
.field public static final CM_SERVER_ADDRESS : L java/lang/String ; = " cm-ge.xlink.cn "
.field public static final CM_SERVER_PORT : I = 0x5ce2
Господи, может ли это быть необработанный протокол на основе сокетов? Я попробовал и, конечно же, смог открыть TCP-соединение с cm-ge.xlink.cn:23778
. Однако Smali также был пронизан логикой для пакетов UDP , поэтому я не был уверен, какой протокол будет использовать приложение. Имея это в виду, я создал пакетный прокси и настроил его на прослушивание порта 23778. Затем я заменил домен cm-ge.xlink.cn
своим IP-адресом в коде Smali, перекомпилировал приложение в APK и установил его на мой телефон.
Разумеется, мое пропатченное приложение C by GE немедленно подключилось к моему пакетному прокси-серверу и начало общаться. Примечательно, что это происходило только тогда, когда Bluetooth и Wi-Fi были отключены. В остальном, похоже, он предпочитал один из них для локального общения с умными лампочками.
Протокол, который выбрало приложение, оказался самым простым: 1) это был TCP, а не UDP, 2) он был полностью незашифрован. Оглядываясь назад, отсутствие шифрования вызывает тревогу, поскольку первое сообщение включает в себя токен авторизации, который, похоже, никогда не меняется для моей учетной записи.
Я обнаружил, что сообщения из приложения на сервер можно эффективно «воспроизвести». Как только я выяснил, какие байты (или «пакеты», благодаря пакетному прокси) предназначены для включения и выключения света, я мог просто открыть новый сокет и отправить те же самые пакеты и получить те же результаты. Это был отличный знак. В худшем случае у меня уже был способ реализовать то, что я хотел для себя, даже если это не было бы очень общим.
На этом этапе пришло время углубиться в протокол. После сочетания экспериментов с пакетным прокси и изучения дизассемблирования Smali у меня сложилось довольно общее представление о том, какая связь происходит. Первое, что я заметил, это то, что общение происходило в «сообщениях», которые начинались с поля типа и длины (с прямым порядком байтов). Следующим шагом было выяснить, какие типы пакетов и какие именно, и, в конечном итоге, как были закодированы сами конкретные пакеты. Вот пример пакета с сервера, содержащего статусы трёх моих устройств:
73 00 00 00 60 47 e2 be ab 00 37 00 7e 00 01 00 00 f9 52 4e
00 03 00 00 00 03 00 03 00 81 01 00 00 81 01 00 00 00 00 35
00 00 00 27 00 00 00 00 00 00 00 02 00 00 01 00 00 00 01 00
00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 01 00 00 01 00
00 00 01 00 00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 c8
7e
Как только я достаточно проработал протокол, я создал для него API. Этот API может составлять список устройств, получать их статусы и обновлять различные свойства устройств (например, яркость и цветовой тон). Удивительно, но я обнаружил, что мой API намного быстрее и надежнее, чем само приложение. Похоже, что попытка использовать Bluetooth или Wi-Fi перед возвратом к удаленному серверу приводит к тому, что приложение становится гораздо более нестабильным и менее надежным, чем могло бы быть.
И последнее замечание: я не владею всеми устройствами, поддерживаемыми этим приложением, поэтому у меня не было мотивации (или возможности) перепроектировать, как эти устройства будут работать. Например, одна и та же компания производит умные розетки, датчики и выключатели света.