История реверс-инжиниринга одного пушистого зверька

:

Тихим утром третьего января, когда Москва уже дремала после новогодних праздников, в нашей квартире раздался звонок в дверь. Почта наконец-то доставила посылку с новогодними подарками, заказанными на Амазоне. Среди прочего в ней находился и подарок для сына — электронный питомец Furby. Покупка его была, в общем-то импульсной. Игрушка значилась в бестселлерах новогоднего сезона и стоила относительно недорого. В сортах Furby я не разбирался, но когда-то давно что-то позитивное об игрушке слышал.

Сынишку, в силу его годовалого возраста, подарок не сильно впечатлил, а позволять бросать сложное электронное устройство на пол и отрывать этому устройству уши мне было жалко, и все шло к тому, чтобы убрать подарок на полку до лучших времен, однако мой взгляд пал на одну надпись на красочной упаковке...

Надпись гласила, что для данной игрушки в AppStore можно скачать приложение, с помощью которого киберпитомца можно кормить, подавать ему всякие команды, а также переводить фразы, которые он произносил на своем языке — фурбише (Furbish), на английский. Приложение было скачано, питомец покормлен всякими съедобными и несъедобными объектами, которые он либо с аппетитом проглатывал, либо выплевывал, а переводчик с фурбиша на английский работал на удивление точно.

Неужели распознавание аудио работает в наше время так надежно и даже в довольно шумной обстановке? Что-то тут не так. И как приложение передает команды Furby? ИК отпадает (ранние версии Furby, как выяснилось, имели ИК-порт для общения между собой), Bluetooth тоже. Остается только аудио. Это интересно… Вот если бы удалось хакнуть протокол общения с этим созданием и уметь управлять им с компьютера… Найти какие-то «пасхальные яйца», скрытые или сервисные команды! Или…

В общем, как вы поняли, отец семейства сделал себе на Новый Год подарок.

***

Для начала синхронизировал iPhone с компьютером и заглянул внутрь файла приложения (.ipa). Среди прочей требухи там нашлось несколько десятков коротких WAV-файлов, пронумерованных особым образом. Все это очень смахивало на готовые аудиокоманды. Первый файл начинался с номера 350. После воспроизведения этого файла в Audacity Furby чего-то деловито пожевал и выдал радостное «Mmm, yum!». «Ага!» — подумал Штирлиц, — «Теперь-то ты у меня наешься!».

Команды в приложении начинались с 350-й и заканчивались 900-й, с большими пробелами в нумерации. Значит потенциально Furby умеет воспринимать гораздо большее число команд, чем есть на руках этих готовых WAV-файлов. Надо искать дальше.

Внешний вид сигнала в Audacity наводил на мысль, что используется какая-то частотная модуляция, причем шел один сигнал, дальше небольшая пауза, затем визуально такой же сигнал снова. Общая продолжительность — полторы секунды. Раз модуляция частотная, то неплохо бы взглянуть на спектр. Посмотрел график — на нем отчетливо выделялось пять пиков на одинаковых расстояниях друг от друга в районе 16-19КГц:

Башня из Мордора — это, конечно, красиво, но как это расшифровать? Покопался в Audacity еще немного и отрыл режим отображения аудио в виде спектрограммы. Вот эта картинка уже была гораздо красивее первой:

Здесь отчетливо видны две посылки с паузой посередине, отличающиеся друг от друга порядком следования «нот» (базовых частот). Причем средняя частота является несущей, постоянно чередуясь с другими четырьмя «нотами».

Для удобства декодирования последовательности сделал в графическом редакторе маску, которую наложил поверх скриншота спектрограммы, присвоил каждой ноте последовательно числа от 0 до 3 и начал анализировать последовательно идущие команды (как мы помним, разработчики iOS-приложения услужливо пронумеровали нам все WAV-файлы). Поначалу оказалось, что в соседних командах числа иногда «прыгают», т.е. идут не так, как хотелось бы при последовательном инкрементировании чисел. После некоторого анализа стало понятно, что «ноты» надо нумеровать так, как на рисунке ниже:

Здесь посылка расшифровывается как 3233 3012 1032 (для удобства восприятия я разбил последовательности на блоки по четыре цифры; в четверичной системе каждый такой блок — это один байт).

Дальнейший анализ команд, перевод их бинарную форму и побитовое сравнение выявило следующую структуру посылки и команды в целом:

  1. Первый байт (в примере это 3233), будучи записанным в бинарном виде, имеет следующую структуру: 11 1 01111, где старшие два бита всегда равны 11, следующий бит равен 0 для первой посылки в команде и 1 для второй, а 01111 — это сами данные (часть идентификатора команды);
  2. Второй байт (3012) — контрольная сумма, зависящая от 6 бит команды (где 1 бит — идентификатор посылки и 5 бит — сами данные);
  3. Третий и заключительный байт посылки всегда равен 1032.

Что это значит? Во-первых, команда разбивается на два пакета по 5 бит данных в каждом. В сумме мы получаем 10-битное число, т.е. потенциальное число команд, которые может посылать или принимать Furby — 1024. Однако метод вычисления контрольной суммы вычислить не удалось. Проанализировав номера команд получилось, что я могу на основе имеющихся WAV-файлов найти 7 из 32 контрольных сумм для первой посылки и 31 из 32 контрольных сумм для второй посылки. В сумме это давало 217 потенциальных команд вместо имеющихся 76 (в виде готовых WAV-файлов), что тоже неплохо.

Написал скрипт, который генерировал по нужному номеру команды WAV-файл, подобный готовому, и начал перебирать доступные мне диапазоны команд. Как оказалось, недокументированные команды действительно были — Furby реагировал на них разным образом, пел песенки, читал рэп, чихал, имитировал сон и делал прочие незамысловатые вещи.

Это подстегнуло исследовательский аппетит, однако алгоритм вычисления контрольной суммы реверс-инжинирингу упорно не поддавался, а значит большая часть команд оставалась для меня недоступной.

В очередной раз прочесывая Интернет на предмет каких-нибудь зацепок я вдруг обнаружил ссылку на официальное приложение Furby для Android (про которое на коробке с игрушкой не было ни слова). «Android → Java → байт-код → исходники → … → ПРОФИТ!». Никогда еще Штирлиц не был так близок к разгадке…

***

Найдя наконец на какой-то помойке нужный мне .apk-файл, я залез внутрь и не увидел ни одного WAV-файла с командами, хотя в целом набор ресурсов был похож на тот, что в приложении для iOS. Раз WAV-файлов нет, то приложение как-то генерит команды на лету? Это то, что мне нужно! Декомпиляция и просмотр Java-кода дал несколько интересных зацепок, но как оказалось, вся интересная начинка, а именно генерация и анализ аудио, находится внутри нативной .so-библиотеки, в которой есть один метод, который мне был нужен, а именно private static native byte[] GenerateComAirCommand(int paramInt);.

Как же достучаться до нативного метода? Пораскинув мозгами, Штирлиц решил качать Android SDK. В итоге был собран маленький проект, в который включена сама нативная библиотека и минимальная обвязка, предоставляющая доступ к одной только нужной мне функции. Само же приложение при старте просто создавало WAV-файлы для минимально необходимого мне набора WAV-файлов, где в командах были те самые недостающие старшие и младшие 5 бит, для которых мне были нужны контрольные суммы. После некоторого курения Stack Overflow (опыта написания приложений под Android у меня на тот момент не было) приложение запустилось и сгенерировало мне на виртуальной SD-карте эмулятора набор нужных мне WAV-файлов, которые я перетащил через adb pull в нормальную файловую систему. Анализ этих файлов дал мне полное покрытие — все 64 контрольных суммы, по которым можно воссоздать любую из 1024 команд.

В ходе анализа реакций Furby на команды был найден еще один диапазон команд, на который Furby так или иначе реагировал. Однако каких-то атомарных команд типа «открой глаза», «закрой глаза», «пошевели ушами» обнаружено не было. Равно как и не была найдено команд самоуничтожения или зачитывания EULA на фурбише (это не значит, что каких-то специализированы команд нет, просто они могут активироваться, например, особой последовательностью или вообще другим набором кодов — но это как-то выяснить вряд ли возможно).

***

Однако я решил пойти дальше и написать анализатор ответов Furby, так как некоторые команды, хотя и не дают видимого результата, могут вызывать реакцию Furby в виде ответных команд, что тоже интересно. В итоге был написан Perl-скрипт, анализирующий поток PCM-данных с микрофона, делающий на лету и расшифровывающий эти посылки. Писалось все это под Windows, где для Perl, к сожалению, нету нормальных способов записи данных с микрофона, поэтому пришлось сделать консольную программу на Delphi, которая читает с микрофона данные и выводит их непрерывно в STDOUT. Поток данных перенаправляется в скрипт, где уже происходит анализ. Такой вот Unix way для Windows.

«Стоп, стоп, стоп,» — скажет утомленный читатель, — «А для чего все это нужно?»

Мне было интересно посмотреть, «что у него внутри», не ломая игрушку физически (все-таки покупал не себе). Попутно получил знания о генерации и анализе звука в Perl, о FFT, оконных функциях, о работе с Android, что само по себе увлекательно.

Возможно кому-то данная статья пригодится при реализации собственного протокола, есть ведь всякие интересные примочки для iPhone, передающие данные как раз через аудиоразъем.

Ну и, наконец, возможность управлять Furby через компьютер потенциально открывает эмоциональный метод нотификации о каких-то событиях. Например, при приходе почты от определенного адресата можно попросить Furby что-то станцевать, по приходу коммита в Git от определенного человека — помурлыкать, а от другого — издать звук менее приличный (коих у Furby есть в запасе). Правда для этого все же нужно решить уже парочку задач хардверных. Во-первых, запретить Furby засыпать через 10 минут неактивности (а активностью считается физическое тормошение — для этого у него имеется датчик положения в пространстве) и питать его не от батареек, а от блока питания или USB. Может быть на Хабре есть знатоки железа, которые захотят окончательно укротить зверька?

Сам код после некоторого причесывания выложен на GitHub. Пожелания и находки всячески приветствуются. Разумеется, вся изложенная информация и программный код предоставлен исключительно в образовательных целях.

Oo-tah-toh-toh. Kah way-loh.