Расшифровывая Siri

Ребята из французской компании Applidium ровно через месяц после релиза iPhone 4S отреверсили протокол персонального помощника Siri. Ниже перевод статьи, в которой описан сам процесс реверс-инжинеринга и те интересные факты, которые открылись исследователям.

14 октября 2011 Apple представила новый iPhone 4S. Одной из новых возможностей была система Siri – персональный помощник. Siri обрабатывает запросы на естественном языке для взаимодействия с пользователем.

Apple повествовала о том, что Siri отправляет данные на удаленный сервер (вероятно, поэтому Siri только работает поверх 3G или WiFi). Как только наши руки получили новенький iPhone 4S, мы решили узнать, как эта штука работает.

Сегодня нам таки удалось сломать и открыть протокол Siri. Как результат, теперь мы можем использовать движок распознавания Siri на любом девайсе. Да, это значит, что теперь любой сможет написать приложение для Android, которое будет использовать тот самый Siri! Или использовать Siri на iPad. И мы хотим поделиться своими знаниями с вами.

Демонстрация


Наверное, это наиболее удачное демо функции преобразования речи в текст через Siri. Мы создали простую звукозапись, на которой произносится фраза “autonomous demo of Siri” (автономное демо Siri), и получили замечательный результат!

Sample_Siri_speech_to_text.zip (70.78K)

Этот сэмпл никогда не проходил через какой-либо iPhone, но, несмотря на это, у нас получилось заставить Siri проанализировать его для нас.

Копаемся в протоколе – краткое техническое введение


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

Самый простой способ перехвата HTTP-трафика – создать подконтрольный тебе прокси-сервер, настроить iPhone на его использование, и смотреть на то, что проходит через наш прокси. Таким образом, мы нацепили tcpdump на сетевой шлюз, и поняли, что трафик Siri отправлялся по TCP на порт 443, на сервере 17.174.4.4.

Перейдя по 17.174.4.4 на десктопе мы заметили, что этот сервер предоставляет сертификат guzzoni.apple.com. Так оказалось, что Siri обращался к серверу guzzoni.apple.com по протоколу HTTPS.

Как вы знаете, «S» в HTTPS означает «безопасный»: весь трафик между клиентом и сервером зашифрован HTTPS. Поэтому мы не могли просто так читать его с помощью сниффера. В таком случае, самое простое решение – подделать HTTPS, используя поддельный сервер DNS, и посмотреть на то, что приходит на наш сервер. Ребята, которые стоят за Siri, сделали все правильно: они проверяют, что сертификат guzzoni является действительным, так что можно и не притворяться. Ну… они сделали проверку на то, что он в силе, но вы же можете добавить и свой собственный «корневой сертификат», который позволит пометить любой сертификат, и сделать его валидным.

Поэтому все, что нам было нужно, так это установить пользовательский SSL центр сертификации, добавить его в наш iPhone 4S, и подписать с его помощью сугубо наш поддельный сертификат, как «guzzoni.apple.com». И это сработало: Siri оправлял команды на наш собственный HTTPS сервер! Похоже, кто-то в Apple все-же упустил эту деталь.

Именно тогда мы поняли, что протокол Siri является непрозрачным. Давайте посмотрим на Siri HTTP запрос. Тело запроса является двоичным (мы поговорим об этом позже), и вот заголовки:

ACE /ace HTTP/1.0
Host: guzzoni.apple.com
User-Agent: Assistant(iPhone/iPhone4,1; iPhone OS/5.0/9A334) Ace/1.0
Content-Length: 2000000000
X-Ace-Host: 4620a9aa-88f4-4ac1-a49d-e2012910921


Несколько интересных вещей, которые можно из этого извлечь:

  • Запрос использует пользовательский метод “ACE”, а не более привычный GET.
  • URL тоже запрашивается как “/ace”.
  • Content-Length почти 2 Гб. Что, очевидно, не соответствует стандарту HTTP.
  • X-Ace-host это что-то напоминающее GUID. После экспериментов с несколькими iPhone 4S, мы поняли, что это значение, скорее всего, связано с реальным устройством (очень похоже на UDID).


Теперь давайте перейдем к телу. Тело содержит сырые двоичные данные. Когда мы впервые посмотрели на него глазами шестнадцатеричного редактора, мы заметили, что оно всегда началось с 0xAACCEE. Кажется, это заголовок! К сожалению, мы ничего так и не поняли из того, что было после него.

Вот тогда мы взяли некоторое время, чтобы подумать. Как люди, которые занимаются разработкой мобильных приложений, мы знаем; есть одна вещь, которая очень важна, когда речь идет о работе с сетью – это сжатие. Пропускная способность часто ограничена, поэтому, как правило, очень хорошая идея – сжимать все, что мы передаем. А какая самая распространенная библиотека для сжатия данных? zlib: «zlib.net». Эта библиотека действительно эффективная и мощная (и конечно, она наполовину француженка!). Поэтому мы попытались прокачать наши бинарные данные через zlib. Но ничего не вышло, нам не хватало zlib-заголовка. Вот тогда мы думали: «хммм, мы уже имеем этот AACCEE-заголовок в теле запроса. Может быть, есть что-то еще? ». Нам, разработчикам, нравится держать данные упакованными. Три байта это не очень хорошая длина для заголовка. Пусть будет четыре. Таким образом, мы попробовали распаковать данные после четвертого байта. И это сработало!

Теперь, когда мы распаковали данные, мы получили какие-то новые двоичные данные. Не очень понятно как, но некоторые части этих данных были текстом. Среди них наше внимание привлек bplist00. Ура! Это, наверное, какие-нибудь бинарные данные plist. После того, как мы наигрались с этим двоичным потоком, мы поняли, что он состоял из следующих частей:

  • Части, начинающиеся с 0x020000xxxx являются “plist”-пакетами, хххх – это размер двоичных данных plist, за которыми следует заголовок.
  • Части, начинающиеся с 0x030000xxxx это “ping”-пакеты, отсылаемые iPhone’ом на сервера Siri, чтобы поддерживать соединение. Здесь хх это номер ping-последовательности.
  • Части, начинающиеся с 0x040000xxxx являются “pong”-пакетами, отсылаемые сервером Siri в качестве ответов на ping-пакеты. Аналогично, хх это количество номер pong-последовательности.


Расшифровка содержания бинарных plist очень проста, вы можете сделать это на Mac OS X с помощью команды “plutil” (через командную строку). Или в Ruby с помощью gem’а CFPropertyList на любой другой платформе.

То, что мы узнали


Мы действительно узнали несколько интересных вещей о том, как iPhone 4S общается с серверами Apple:

Аудио данные

iPhone 4S действительно отсылает необработанное аудио на сервер. Оно сжато с помощью аудиокодека Speex, который имеет смысл, поскольку этот кодек специально предназначенный для VoIP.

Идентификация

IPhone 4S рассылает идентификаторы повсюду. Так что если вы хотите использовать Siri на другом устройстве, вам все равно придется иметь идентификатор, по крайней мере, одного iPhone 4S. Конечно, мы не будем публиковать наши, но его очень легко получить, используя инструменты, о которых мы уже писали. Конечно, Apple в теории может внести в черный список идентификатор, но пока вы используете его в личных целях, все должно быть в порядке.

Фактическое содержание

Протокол, на самом деле, очень, очень болтливый. Ваш iPhone посылает тонны вещей, на сервера Apple. И эти сервера отвечают ему невероятным количеством информации. Например, когда вы используете преобразование текста в речь, сервер Apple даже присылает оценку доверия и временную метку для каждого слова.

Что дальше?


Вот коллекция инструментов, которую мы написали, чтобы помочь нам понять протокол. Они написаны, в основном, на Ruby (потому что это удивительно простой язык), некоторые части – на C, а некоторые – на Objective-C. Их разработка на самом деле не закончена, но и этого должно быть достаточного для тех, кто технически может написать Siri-приложение.

Давайте посмотрим, какие забавные вещи вы сделаете с помощью Siri! И давайте понаблюдаем, сколько времени понадобится Apple, чтобы поменять их политику безопасности.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 34

    +19
    Конечно, я смотрю, уже что-то висит подобное на главной, но я делал перевод сегодня ночью, когда еще не было опубликовано ничего. Не сочтите за плагиат, и не пропадать же добру.
      –7
      Ну кривенький перевод и?
        +7
        Перевод неплох. Но вычитка и корректировка, конечно, не помешают.
          +1
          Неплох, для гуглтранслейта)
          «Как только наши руки получили новенький iPhone 4S»
            0
            Оно сжато с помощью аудиокодека Speex, который имеет смысл, поскольку этот кодек специально предназначенный для VoIP.
          –14
          Это конкуренция. В данном случае вы опоздали, в другой раз может больше повезет.
            +12
            Думаю, можно сделать скидку этому автору за то, что ему прошлось сначала написать пост в песочницу, затем дождаться модерации и получения инвайта (+ регистрации на сайте) и уже потом отправки сюда на сайт :)
            +2
            спасибо, было приятно читать.
            +3
            Я не понял в чем Apple нужно поменять их политику безопасности? Если портить корневые сертификаты то какая политика устоит?
              0
              Банить по ID — самое очевидное решение.
                +1
                я имел в виду вот это:

                Поэтому все, что нам было нужно, так это установить пользовательский SSL центр сертификации, добавить его в наш iPhone 4S, и подписать с его помощью сугубо наш поддельный сертификат, как «guzzoni.apple.com». И это сработало: Siri оправлял команды на наш собственный HTTPS сервер! Похоже, кто-то в Apple все-же упустил эту деталь.
                  +1
                  Ну SSL как-бы придуман для того, чтобы данные перехватывать не получалось и сервер Siri подделать не вышло. Но вторую проверку, как известно, можно отключить на клиенте (ну или установить свой сертификат в хранилище). Он не предназначен же для защиты от взлома собственно протокола. Тем более, что после того как они узнали технические детали, можно вообще пользоваться оригинальным сервером/сертификатами и выглядеть всё будет, как честный iPhone 4S.
                  0
                  Очевидное-невероятное. Как быть с «упёртыми» ID?
                  0
                  Имелось в виду проверять сертификат в приложении, а не полагаться на openssl (или какую они там библиотеку юзают) для проверки валидности.

                  Вот, например, ssh никому, кроме себя не верит. И его не убедить, что узел доверенный, если его ключа нет в списке известных хостов.
                    +1
                    Но точно также ничего не меняет добавить ключ. Не хардкодить же отпечатки в бинарнике.
                      +1
                      Как раз хардкодить в бинарнике в данном случае — очевидная процедура. На этапе компиляции просто брать текущий актуальный сертификат (и запасной, который not valid before) и вкоживать.
                        0
                        если я отредактирую known_hosts, то ssh будет обманут абсолютно точно так же, или я неправ?
                          0
                          Именно. Но откуда именно читается этот файл, решает бинарник. И он же решает читать ли его, или использовать хардкоженные ключи.

                          Заметим, что в этом случае подмена будет существенно затруднена, а при минимальных усилиях по защите целостности этого кода, доведена до крайне высокого уровня защиты. Разумеется, бинарник тоже ломаем, но с куда большей кровью.
                            +1
                            ну тогда уж легче просто запретить менять корневые сертификаты но ведь так никто не делает (я даже склонен догадаться почему)
                              +1
                              Запретить менять сертификаты «вообще» — поломать всю PKI (читай, нормальный SSL в браузере). Запретить конкретному приложению пользоваться системными сертификатами и юзать только свои (имеется в виду, иметь свои доверенные корневые сертификаты) менее инвазивно.
                                +1
                                … PS Хотя идея с выпуском своего фальшивого сертификата и добавления себя как доверительного — да, интересно. Очень.
                                0
                                > Но откуда именно читается этот файл, решает бинарник.

                                Не совсем так. Побуду немного занудой:

                                Конфиг /etc/ssh/sshd_config позволяет это настраивать:
                                # For this to work you will also need host keys in /etc/ssh_known_hosts
                                < ... >
                                # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
                                #IgnoreUserKnownHosts yes


                                Т.е. конфиг может быть для каждого пользователя свой или системный/общий для всех. Т.е. некоторая гибкость все-таки присутствует.
                                  +1
                                  Какой файл конфига читать — решает бинарник. Я не говорю про тот конкретный ssh, который у вас из пакета в системе стоит, я говорю о том, что приложение имеет полную свободу решать, каким ключам оно верит, а каким нет. С корневыми сертификатами аналогично.
                      +3
                      Пост в принципе дополняет написанный ранее, т.к. в нем больше информации непосредственно по коммуникации клиент-сервер. После предыдущего у меня еще были сомнения. Спасибо.
                      • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Интересная статья, интересно было читать и спасибо за перевод.
                          0
                          Надежда на появление Siri на Android угасла с прочтением абзаца про UDID :(
                            0
                            На ихнем гитхабе написано:

                            How to do speech-to-text using a non-iPhone4S machine

                            Record your voice into whatever format you like
                            Use ffmpeg to convert the sound to raw sound samples (see the text file for the exact command line). Name it «tentative.raw»
                            Install the speex library and its header. On Mac OS X, «brew install speex» once you've setup Homebrew
                            Compile the speexEnc.m file (gcc speexEnc.m -lspeex -framework Foundation -o speexEnc)
                            Run ./speexEnc. It will produce a input.sif file with speex packets the Ruby script will be able to read
                            Run the «Siri.old.inline.rb» script. Et voilà!
                            • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              >Например, когда вы используете преобразование текста в речь, сервер Apple даже присылает оценку доверия и временную метку для каждого слова.

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

                                > Content-Length почти 2 Гб. Что, очевидно, не соответствует стандарту HTTP.

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

                                > И эти сервера отвечают ему невероятным количеством информации. Например, когда вы используете преобразование текста в речь, сервер Apple даже присылает оценку доверия и временную метку для каждого слова.

                                И что в этом плохого?

                                > И давайте понаблюдаем, сколько времени понадобится Apple, чтобы поменять их политику безопасности.

                                При чем тут безопасность? Максимум что можно получить — потратить немного процессорного времени на сервере Эппл. А да, можно еще написать кривой клиент для андроида, вбив в который свой Эппл id можно будет получить и на том аналог siri. Но зачем?
                                  +2
                                  трафик Siri отправлялся по TCP на порт 443, на сервере 17.174.4.4

                                  А там сидит 10000 человек на зарплате, распознают на слух и отправляют обратно))

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое