Главная » Хабрахабр » Android accessibility — волк в овечьей шкуре? Лекция Яндекса

Android accessibility — волк в овечьей шкуре? Лекция Яндекса

Месяц назад на очередной Droid Party старший разработчик Данила Фетисов подробно разобрал принцип действия службы, которая отвечает за accessibility-функции Android. Вы узнаете о том, как использовать её для улучшения доступности своих проектов, а также об опасной уязвимости под названием clickjacking.

— Меня зовут Данила Фетисов, я из московского офиса Яндекса, конкретнее — из Такси, конкретнее — из Таксометра. Сегодня мы с вами поговорим о том, что же такое Android accessibility и почему же я такую святую штучку для людей с ограниченными возможностями решил назвать волком в овечьей шкуре.

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

Как вообще мы можем это применять? Может быть, расскажу парочку лайфхаков, продуктовых ништяков. В системе происходят какие-либо события, они падают нам в сервис, и мы их обрабатываем. Вообще что это такое, для тех, кто не знает?

Но что самое интересное, мы можем не просто получить, но и закинуть событие системы. Раз нам что-то падает, мы можем взять оттуда контент и озвучить с помощью того же Yandex SpeechKit, Google Text-to-Speech или еще чего нибудь, что вам нравится. Закинул событие, нажал кнопочку, и прямо все отлично, радуешься жизни. Еще можно что-нибудь сказать.

Google и запустил эту фичу для того, чтобы вашим приложением могли пользоваться люди с ограниченными возможностями. Если говорить про эти два пункта, они, по сути, являются дефолтными. И пошло-поехало. Но как все знают, мы, разработчики, скучать не хотим: садимся, смотрим код и думаем: как же можно применить?

Сами понимаете: раз мы получаем контент с экрана, то мы можем понимать, как третировать этого пользователя. Автоматизация, тестирование с помощью имитации действий пользователя, контекстная реклама.

Обработка USSD-запросов. Что еще интересного? И вы представьте, они серьезно парсят USSD-запросы, парсят UI при помощи AccessibilityService. К сожалению, нормальный API появилась только с 26-го SDK, и разработчикам как-то нужно выживать.

Почему? Шестой пункт — многоточие. А дальше буквально открываете свой поток фантазии, берете чашечку чаю, кофе, и погнали кодить свои крутые фичи. Потому что после этого доклада у вас примерно сложится понимание, как это применять и для чего.

Следующий слайд будет до боли банальным.
С чего начать?

Берем сервис, наследуем от AccessibilityService и радуемся жизни.

По сути, обычная запись сервиса. Закидываем потом этот сервис в Manifest, и что же тут интересного? Сейчас я не буду рассказывать, что это такое. Но обратите внимание на поле label. Запомните, это будет чуть позже.

Дальше по Manifest у нас просто файлик конфигов, тоже ничего интересного.

Из того, что вам понадобится, скорее всего, процентов 85–90 — это поле настройки фильтра для входящих событий. А сам файлик конфигов немножечко пострашнее выглядит.

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

Для чего это нужно? Далее — фильтр на пакеты приложений. Поэтому Google решил это как-то ограничить. Вы сами прекрасно понимаете, что получать события от абсолютно всех приложений в системе было бы слишком для одного приложения. Добавил фильтр.

Было поле label, а теперь используют description. Далее description. По сути, они создают пару, которая затем расскажет пользователю, для чего нужно включить ваш сервис.

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

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

Крутой AccessibilityService — это как раз таки и есть тот label из manifest, о котором я вам говорил на предыдущих слайдах.
Сначала открываем ему специальные возможности.

Пользователю нужно выбрать ваш сервис, и тут он начинает читать description, зачем же вы хотите включить этот сервис и получить определенный контроль над устройством.
Дальше.

Но не тут-то было. Окей, включает. Пользователи не всегда все это читают, просто кликают «ОК», чтобы мы от них отстали. Вылезает какой-то страшный диалог, который говорит: «Чувак, если ты сейчас включишь, мы захватим полный контроль над твоим приложением, условно говоря, будет все страшно». Так что живем.

Следующий слайд немножко поумнее первого слайда про сервис, но тем не менее.
Ну, что же, где же обработка событий по факту у нас получается?

По сути, interrupt — такой метод, в котором вы должны почистить ваши ссылочки, остановить подписки или что-нибудь еще. Вот наш DummyAccessibilityService, и мы закидываем туда два метода, в котором будем все писать.

Что же мы можем из него достать?

Для начала мы можем понять, что же, на самом деле, произошло у вас в системе. Самое интересное — это класс AccessibilityEvent, который к нам падает. Берем EventType, и что тут достаем?

Прямо все туда падает на window_state_changed. А достаем, что у нас какая-то Activity там открылась, или Dialog, или Popup, или вообще нотификация свалилась.

Подвинули ее, закинули какую-то строчку в text view или что-нибудь подобное. Далее понимаем, что контент у какой-то вьюхи изменился.

По-моему, 85, да даже 98, наверное, процентов основных продуктовых задач вы покроете при помощи этих event type. А дальше мы узнаем, когда пользователь кликает, или выбирает какую-либо вьюшку.

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

Теперь вопрос у нас: как же найти view? А теперь давайте поймем, с какой же view, с каким компонентом это произошло.

Берем наш AccessibilityEvent.

Берем у него Source, и, вуаля, метаинформация по view уже у нас.

Тут есть ограничение на SDK, но оно мелкое.

Берем наш AccessibilityService, и получаем, по сути, ссылочку на корневой элемент во всей иерархии на текущем экране. Но что делать, если вы хардкорный разработчик, и вам не хватает информации про одну view, вам хочется понять, что в принципе происходит сейчас на экране? Что же нам дальше-то делать? И уже потом достаем нашу мета-информацию по view.

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

Берете ID view, и просто находите одним методом. Если хотите пойти безопасным путем, то, пожалуйста, первый вариант.

Если вам кажется, что это как-то скучновато, охота чего-то повеселее, то берем текст из view, находим по тексту.

Тогда точно поймете все, что происходит. Если и это вам недостаточно интересно — ну, ладно, ребята, берите Child и Recursion, ходите по нему.

Мы с вами поняли, как находить view. Отлично. Как же теперь взаимодействовать с ней?

Какие же Action мы можем туда закинуть?

Для начала мы можем закинуть Click. Опять берем наш AccessibilityNodeInfo, и теперь уже закидываем Action в него. Да потому что есть куча corner case, когда обычный Click вас не спасет. Почему в итоге я сюда поставил не после просто Click, а еще Click и Select? Или же на некоторых девайсах в принципе вьюха не кликабельна, пока Select у нее не вызовешь. Допустим, пользователь выбрал текст, и клик по вьюхе чисто уберет selection с этого текста. И если вы хотите просто париться — делайте Select, делайте Click. В общем, очень много проблем. Два Action, и все, счастье есть.

А дальше у нас установка текста. Что же дальше у нас? Берем Action_set_text, создаем Bundle с нашей строчкой, и живем. Тут тоже все просто.

Опять же, это второй вечер уже у вас будет. И третий пункт — это go в документацию. Куча экшенов прямо, по-моему, еще больше, чем событий.

Самая скучная часть доклада закончилась. Ура! Теперь пошло время всяких ништячков.

Теперь вам нужно понять, как заставить пользователя, как попросить его все-таки включить этот сервис. Допустим, вы создали сервис, настроили его, закинули какую-то обработку событий. Чтобы это понять, вам нужно узнать, в принципе сейчас включен сервис или нет.

Берем AccessibilityManager и спрашиваем у него все включенные на текущий момент сервисы в системе, и там находим наш. Тут до банальности все просто.

Вот тебе пустой список». Но все было бы, конечно, классно, если бы AccessibilityManager был нормальным парнем, и не было такого, что ты у него спрашиваешь: «Чувак, дай мне, пожалуйста, все доступные сервисы», а он говорит: «Извини, я сегодня не в настроении. И ты такой сидишь и думаешь: «Блин, нормально же общались».

Secure.getInt и getString. Ну ладно, есть старые добрые друзья — это Settings. Если включены, то в одну строчку закидываются все, и там уж мы через контент какой-нибудь ищем наш. Сначала мы спрашиваем, включены ли вообще сервисы в системе.

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

Вот просто, ребята, боль! Думал-думал, ресерчил-ресерчил, и тут, бинго, я понял, что это — это Xiaomi с их MIUI. Вот серьезно.

Как же теперь ее решать? Окей, поняли, в чем проблема. К сожалению, на некоторых прошивках в MIUI все сделано так, что если вашего приложения нет в автостарте, то система не будет в принципе стартовать даже ваш сервис.

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

И вот наступила вишенка на торте.

Мало того, что мы сможем ее украсть, мы можем закинуть ее куда-нибудь, допустим, экран разблокировать, если он заблокирован при помощи пин-кода. Мне кажется, многие из вас уже догадались, что если пользователь включит этот сервис, то мы спокойно сможем украсть достаточно много конфиденциальной информации с его устройства. Что дальше? Ну, окей, разблокируем экран. И, в конце концов, можем в принципе сымитировать абсолютно любые действия пользователя, и, допустим, поставить какое-нибудь приложение, выдать ей админские права, и сказать пользователю: «Давай до свидания. Накликаем себе рекламки, заработаем кучу денег, прямо заживем. Рассказал нам, как мы можем какие данные украсть. Прощайся со своим устройством».

Окей, мы можем все это сделать, но вы сейчас скажете: «Ты, конечно, молодец. Окей, пользователь, мне кажется, просто откроет настройки, поймет, что там написан какой-то бред, закроет, удалит приложение и забудет. Но как ты заставишь пользователя включить ваш AccessibilityService, причем если еще приложение какое-то левое?». И тут к нам на помощь приходит уязвимость, которая, по сути, называется clickjacking. И знаете, вы будете правы в том, что первый способ включения через системные настройки сходит на «нет», потому что, сами понимаете, это бред какой-то. Отлично, будет интересно. Ребята, кто из вас знаком в принципе с clickjacking?

По сути, при помощи всего лишь двух permission и минимального взаимодействия с пользователем вы получаете доступ ко всему приложению прямо от слова «совсем».
Но окей, что нам нужно сделать, чтобы все-таки взломать пользователя? Так вот, на основе этой уязвимости некоторые ребята — ссылочку небольшую я приложил, потому что все следующие скрины будут с ресурса этих ребят — придумали атаку, которая называется Cloak and Dagger.

Мы надеемся, что у пользователя Android версии меньше 8. Мы закидываем наше приложение в Google Play.

И все, пользователь скачивает наше приложение.
Сами понимаете, чуть-чуть — 95% на текущий момент.

Сейчас сами все увидите.
Если вкратце, то эта malware рассказывает пользователю, как стать хорошим парнем, показывает в самом конце видюшечку.

Небольшой текст. Окей, стартуем. Окей, конечно, начинаем.
Нам говорят: «Если ты сейчас начнешь tutorial, то станешь хорошим парнем».

Дальше люди будут представлены в виде зеленых человечков. Еще какой-то текст. Ну, ладно, давайте, нажимаем Next.

Ну, ладно. Еще текст. Сейчас я его дочитаю и точно увижу видюшку, на которой мне расскажут, как стать хорошим парнем.

Это нам не столь важно. Нажимаю «Окей», и там запускается видюшка. Пользователь запускает приложение, а так как мы его поставили из Google Play, нам автоматом выдаются permission на overlay. Важно, что на самом деле происходило на устройстве в этот момент. Пользователь кликает на Start Tutorial, а на самом деле выбирает наш AccessibilityService. Вуаля, мы открываем окошко специальных возможностей, и сверху просто overlay. Ну, вы понимаете, что пошло дальше.

Дальше мы нажимаем Next, выбираем тумблер.

В самом конце мы включаем AccessibilityService, и пользователь даже не догадывается, что это произошло.

Overlay устроены таким способом, что они либо полностью поглощают все события, все touch и так далекк, либо они полностью пропускают. Знаете, в чем тут проблема? В данном случае это кнопочка OK. И фишка именно этой атаки в том, что мы заполняем overlay, весь экран кроме одного пространства. Когда мы на нее кликаем, мы получаем ивент, узывающий, что произошел touch outside, и все отлично. Эта кнопочка — действительно из диалога.

Сами понимаете, это достаточно жестокая уязвимость.

Началось все достаточно интересно — все с восьми лет молчания. Как же Google реагирует на все эти проблемы? Это безопасно. Google зарелизил эту штучку, сказал: «Ребята, пользуйтесь. Ну окей. Точно говорю». Те же самые ребята, которые Cloak and Dagger придумали, закидывали и саппорт, и issue trackers, и bug trackers. Google закидывали.

Я закрою clickjacking в Oreo». Потом Google сказал: «Ладно, ладно, ребята, успокойтесь. Сидят такие еще, думают: «Ну, блин, ребята, все-таки если злоумышленники заставят пользователя включить ассеssibility, это будет плохо. Ну ладно, закрыли. Давайте-ка немножечко над разработчиками поиздеваемся.

Если кратко сказать, то в письме было следующее: «Ребята, расскажите нам и пользователям, для чего вам нужны AccessibiiltyService». И отправляют письмо: «Сделайте то, не знаю, что, иначе ваше приложение будет удалено из Google Play». В каком формате? Как рассказать? Вообще нет понимания. Где? И в письме, конечно же, это не указано.

Громадные треды на reddit, куча писем в саппорт. Ну окей. Закиньте в description, который я вам показал, в manifest, закиньте в описание в Google Play, и все будет нормально, я от вас отстану». И потом какой-то разработчик выкладывает на reddit письмо, и в нем говорится: «Да там, ребята, ничего сложного.

А что если я молодой разработчик, только познакомился с AccessibilityService и хочу зарелизить приложение с этим? Я в этот момент подумал: ну окей, нашел я это письмо в каких-то кулуарах Reddit. д.? Откуда я должен узнать, что мне нужно произвести такие действия, нужно в description, в Google Play закинуть т. Думал-думал, искал-искал и, знаете, ребята, ничего не нашел, от слова совсем.

Есть просто описание в основной документации: «Ребята, это чисто для людей с ограниченными возможностями и точка. Ну, серьезно, ничего нет. А на самом-то деле, оказывается, можно.

Что ж, мы с вами подошли к концу. Больше никак использовать нельзя». Мы проговорили, как это можно использовать, поняли, как это все создавать, настраивать, поняли, как обрабатывать UI-события. Что же мы смогли с вами проговорить? Конечно, я вам не рекомендую так делать, это было просто для информации. И, конечно, поняли, как можно, но не нужно взаимодействовать с AccessibilityService. Вот обещанные источники:

— developer.android.com/guide/topics/ui/accessibility/services
— developer.android.com/reference/android/view/accessibility/AccessibilityEvent
— developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo
— cloak-and-dagger.org

Ребята, спасибо большое за внимание!


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Спустя пять лет вышла очередная версия DOSBox под номером 0.74-2

К сожалению, пока внимание толп людей было приковано к анонсам таких гигантов индустрии как Apple, релиз одной некоммерческой, но довольно популярной игровой ретро-платформы, прошел почти незаметно. Вышел DOSBox 0.74-2. Это позволяет играть в такие игры во множестве операционных систем, не ...

Данные пользователей Windows на ПК с поддержкой сенсорного ввода пишутся в отдельный файл

Это сделано для удобства пользователя и ускорения процесса его работы. Большое количество моделей ноутбуков и all-in-one рабочих станций в наше время имеют поддержку сенсорного ввода. Но, как оказалось, у компьютерных систем с активированной поддержкой тач-ввода есть одна малоизвестная функция, которая ...