Основы безопасности операционной системы Android. Уровень ядра
Вступление
Самой распространенной операционной системой для смартфонов на сегодняшний день является Android. Но не только этот факт подогревает интерес к ней. Открытость, возможность что-то настроить, подкрутить, и, естественно, сломать тоже в немалой степени способствуют увеличению популярности этой платформы. Я попробую поделиться опытом, как устроена эта операционная система, а так же рассмотреть систему безопасности. Всем, кому интересно, добро пожаловать! В этой статье я рассмотрю безопасность на уровне ядра.
Disclaimer
Термины я буду стараться писать на английком языке, так как боюсь ошибиться в их переводе. Если кто-то знает, как их красиво перевести на русский язык, напишите мне, и я дам перевод этих терминов. Желательно, чтобы у вас под рукой находились исходный код Android (хотя я и буду стараться давать ссылки на файлы в Интернете), потому что я иногда буду давать ссылки на файлы, где находится та или иная функциональность. Как загрузить исходный код, можно почитать здесь или вот в этой статье на Хабре.
Список статей
Стек Android
Native Libraries. К этому слою относятся различные нативные библиотеки, которые необходимы для работы Android. Они так же позаимствованы у open-source сообщества. Среди них мы можем найти SQLite, WebKit и т.д.
Android Framework. К этому слою относится то, с чем мы обычно взаимодействуем, когда пишем наши приложения для Android (PowerManager, ActivityManager, NotificationManager и т.д.).
Applications. Приложения бывают двух типов: те, что поставляются вместе с образом системы (системные) и приложения, которые мы загружаем из маркета или других источников. В первом случае, в устройстве приложения находятся в «/system/app» директории, во втором случае в «/data/app».
Безопасность на уровне ядра
Во время установки, Android каждому приложению по умолчанию присваивает уникальные user ID (UID) и group ID (GID), таким образом каждому приложению в этой операционной системе соответсвует свой пользователь. Имя пользователя обычно имеет формат app_x, а идентификаторы пользователя вычисляется по формуле (Process.FIRST_APPLICATION_UID + x), Process.FIRST_APPLICATION_UID равен 10000. Эти идентификаторы приложения не изменяются. Список установленных приложений хранится в файле «/data/system/packages.list» и если у вас рутованый телефон, или вы работаете с эмулятором, то вы можете просмотреть этот файл, используя следующую комманду:
У каждого приложения есть своя домашняя директория, например /data/data/
— имя Android пакета, например com.ex.ex1 Имя Android пакета задается в свойстве package в файле AndroidManifest.xml Эта папка — Internal storage (внутреннее хранилище), директория, где приложение хранит все свои приватные данные, и к которому разработчики приложений получают доступ используя функции Context.getFilesDir() или Context.getDir() У этой папки права доступа определены как drwxr-x—x, т.е. только владелец и пользователи входящие в группу владельцев имеют полный доступ к этой папке. А так как каждое приложение определено как уникальный пользователь, то это означает, что приложения, по умолчанию, не имеют доступа к информации друг друга. Хотя при создании файла во внутреннем хранилище можно явно задать, что этот файл будет MODE_WORLD_READABLE и/или MODE_WORLD_WRITABLE
Кроме того, на уровне ядра уникальные UID и GID каждого приложения используются для разделения доступа к ресурсам системы (память и процессорное время). Таким образом, на уровне ядра для каждого приложения создается своя собственная песочница (Application Sandbox).
С другой стороны, разработчик приложения может указать, что некоторые ЕГО приложения должны иметь один и тот же UID. В AndroidManifest.xml файле для этого есть специальное свойство sharedUserId В этом случае, эти приложения будут иметь доступ к ресурсам друг-друга, но только если они подписаны одним и тем же ключом разработчика.
Некоторые permission (разрешения) так же работают на уровне ядра. Давайте, например, рассмотрим наиболее используемое разрешение android.permission.INTERNET Если приложение запрашивает это разрешение, то Android во время установки дополнительно включает это приложение в специальную группу «inet». Так же работают и некоторые другие разрешения. Список соответствия между этими разрешениями и соответствующими группами можно найти в файле frameworks/base/data/etc/platform.xml:
Список соответствия между именами этих групп и значениями (GID) задан в явном виде в файле system/core/include/private/android_filesystem_config.h в массиве структур android_ids[]:
Таким образом, если приложение пытается подключиться к Интернету, ядро проверяет, находится ли это приложение в группе с идентификатором AID_INET. Если нет, то приложению запрещается доступ. Код этой проверки очень тривиальный:
Заключение
Это моя первая статья на Хабре, так что не судите строго. Если сообществу интересно, то я продолжу в следующих статьях описывать внутренности Android. Я понимаю, что много не знаю, да и времени всегда не хватает, но я постараюсь поделиться тем, что уже пропустил через себя. Надеюсь, что узнаю что-то новое из комментариев! Если кому-то интересна какая-то определенная тема, то пишите в комментариях, постараюсь в будущих статьях учесть ваши пожелания.
Как безопасно пользоваться Андроидом
Всё, что вы хотели знать о безопасности самой популярной мобильной операционки
Я шесть лет пользовался Айфонами, но в сентябре купил смартфон на Андроиде.
Знакомые предупреждали, что Андроид небезопасный: мол, там вирусы в каждом приложении, а опасность поджидает там, откуда не ждёшь. Я решил разобраться, так ли это, и собрал команду знатоков:
Вот что я от них узнал.
Действительно ли Андроид опасен?
Андроид менее защищен, чем Ай-ос. Эта операционная система позволяет устанавливать приложения из сторонних магазинов, обновления прошивок выходят не у всех производителей, а у пользователей есть прямой доступ к файловой системе.
Как не попасться на удочку хакеров
Но у Андроида много плюсов. Во-первых, приложения работают в так называемой «песочнице» — специальном виртуальном пространстве, где программа работает отдельно, а операционка — отдельно. Во-вторых, система безопасности Андроида основана на правах доступа.
Если соблюдать правила безопасности и следить за тем, что ты пускаешь в свой телефон, телефон на Андроиде не менее защищен, чем Айфон.
Какой телефон на Андроиде сейчас самый незащищенный?
Опаснее всего смартфоны со старым версиями Андроида. Гугл не выпускает обновления безопасности для Андроида старше версии 4.4.4. В пятой и шестой версиях есть усиленные настройки безопасности.
Чтобы посмотреть, на какой версии Андроида работает ваш смартфон, перейдите в → «Настройки» → «О телефоне» → «Версия Андроид»:
Этот телефон более-менее защищен: на нем одна из недавних версий Андроида
Самое главное, чтобы для смартфона часто выходили обновления. Большинство смартфонов на Андроиде обновляются редко. Исключение — устройства, которые выпускает Гугл: «Нексусы» и «Пиксели». Еще есть специальные модели смартфонов с упором на защиту пользовательских данных. Например, линейка Андроид-смартфонов «Блэкберри»: Priv, DTEK50, DTEK60. Часто обновляются «Самсунги» и «Эйч-ти-си», однако старые модели в какой-то момент перестают получать обновления.
Также в группе риска дешевые китайские смартфоны — как правило, они привязаны к собственному магазину приложений — аналогу «Гугл-плея», который не так безопасен. Часто в такие смартфоны устанавливают рекламные и шпионские программы.
Опасно пользоваться купленными с рук смартфонами, если вы не обнулили их прошивку и не удалили всё наследие предыдущего владельца. Если вы купили телефон с рук, восстановите заводскую прошивку. Если не можете сделать это самостоятельно, обратитесь в сервисный центр, там это сделают за 15 минут.
Чтобы сбросить настройки, перейдите в пункт «Восстановление и сброс» настроек вашего смартфона:
При покупке смартфона с рук стирайте его до основания
Из-за чего чаще всего взламывают телефоны?
Из-за невнимательности пользователя. Он может загрузить вредоносную программу, открыв ссылку в сомнительном письме, перейти по ссылкам из спама и странных сообщений из мессенджеров или попытаться заполучить доступ к свежему приложению и установить его из сомнительных источников.
Как защититься от сетевых хулиганов в общественных местах
Социальная инженерия — самый простой способ атаки на смартфон. Это способ атаки, в котором человек сам отдает мошенникам свои данные: например, сообщает номер карты якобы сотрудникам банка; или вводит логин и пароль от соцсети в сомнительном приложении. Если устанавливать приложения не из «Гугл-плея», вводить данные от соцсетей и банковских приложений на незнакомых сайтах, то Андроид не спасёт, каким бы безопасным он ни был.
Не открывайте ссылки из сомнительных писем:
Письмо сомнительное, потому что мы никаких платежей не ждали, а ссылка ведет на странный сайт
Отключите в настройках безопасности пункт «Разрешить установку приложений из неизвестных источников»:
Теперь приложения можно устанавливать только из официального магазина
Когда устанавливаете приложение, проверяйте, какие данные оно запрашивает. Например, если фонарику нужен доступ к телефонным звонкам, сообщениям, это повод насторожиться, потому что фонарику для работы нужна только вспышка. Изменить разрешения можно в настройках приложений смартфона:
Фонарику нужен доступ к камере, чтобы включить вспышку. А вот к вашим контактам — не нужен
И, конечно, не сообщайте никому свои личные данные и коды из смс. Остерегайтесь универсальных мессенджеров, приложений типа «Музыка ВК» или «Кто был на вашей странице». Любые неофициальные приложения, которые запрашивают ваши личные данные, могут использовать их против вас.
Нужен ли на телефон антивирус?
Антивирусы защищают от некоторых угроз — они проверяют устанавливаемые приложения и флеш-карты. Но телефон «из коробки» со свежей версией Андроида не нуждается в антивирусе — в этом случае антивирус будет просто потреблять ресурсы телефона. Если у вас старый телефон и на него не приходят обновления, то стоит установить антивирус.
Андроид ограничивает возможности приложений, поэтому большинство современных антивирусов бесполезны. Если вы устанавливаете приложения только из «Гугл-плея», то антивирус, скорее всего, не принесет ощутимой пользы: он будет всё время говорить вам, что угроз не обнаружено.
Телефон постоянно предлагает обновиться. Что делать?
Обновления прошивок смартфона исправляют критические уязвимости в безопасности. Старайтесь пользоваться последней версией прошивки. Убедитесь, что обновиться предлагает именно телефон, а не сторонняя программа. Если всё нормально, то обновляйтесь.
Почему не все обновления программ одинаково полезны
Но прежде чем устанавливать новую прошивку, убедитесь, что она работает нормально. Почитайте отзывы в интернете от пользователей, которые уже обновились. Часто бывает так, что в обновлении устраняют одну старую проблему и создают три новых.
Прежде чем обновляться, сохраните все важные данные — если что-то пойдет не так, вы сможете восстановить информацию. Андроид, скорее всего, уже сделал резервную копию ваших контактов, а вот фотографии лучше сохранить отдельно.
Как сохранять данные?
В настройках смартфона есть пункт «Восстановление и сброс». Проверьте, включён ли тумблер «Резервирование данных». Это встроенный в Андроид бэкап — он сохраняет резервные копии данных приложений, паролей от вай-фая, закладок Хрома.
Контакты из адресной книги и почты Гугла, события в «Гугл-календаре», покупки в «Гугл-плее» сохраняются автоматически, если включена синхронизация аккаунта. Это можно проверить в настройках аккаунтов смартфона.
Чтобы сохранить фотографии, пользуйтесь автозагрузкой в приложении «Гугл-фото». Эта программа загружает ваши снимки и видео в облако.
В последних версиях Андроида для смартфонов линейки «Пиксель» есть полный бэкап данных в «Гугл-драйв». Но эти модели в России официально пока не продаются.
Что совершенно точно не стоит делать в интернете с мобильного телефона или планшета?
Не устанавливайте приложения не из «Гугл-плея» или дополнительные надстройки с сомнительных сайтов. Внимательно относитесь к данным учётных записей соцсетей и банковских приложений — убедитесь, что сайт или приложение действительно официальные, а соединение безопасно.
Часто злоумышленники подделывают сайты банков, а пользователи не замечают подвоха.
Чтобы убедиться, что соединение с сайтом безопасно, обратите внимание на иконку с замком слева от адресной строки Хрома:
Будьте осторожны на порносайтах, сайтах с бесплатными играми и сериалами, сайтах с торрентами, онлайновыми казино. На них вам могут предложить установить обновление, почистить телефон, продлить срок службы батареи или что-нибудь еще многообещающее. Чаще всего за всеми этими чудо-программами будут скрываться трояны, вирусы или агенты ботнета. Если что-то устанавливать — то только из официального магазина «Гугл-плей».
Ничего не покупайте и не ведите важную переписку со смартфона, если пользуетесь публичным незапароленным вай-фаем. Не делайте рутинг телефона, если в этом нет крайней необходимости.
Что такое рутинг телефона? Для чего он нужен?
Андроид работает на основе операционной системы «Линукс». В ней есть пользователь root, у которого есть доступ ко всем объектам системы. От него и появилось слово «рутинг».
Рутинг открывает программам доступ к системным файлам и настройкам. Например, с помощью рутинга можно управлять мощностью процессора или снять полную цифровую копию смартфона со всеми приложениями, настройками и пользовательскими данными. Опытные ребята делают рутинг, чтобы содрать до железа все лишние программы, максимально разогнать телефон, открыть в нём скрытые функции или установить альтернативную прошивку. Звучит заманчиво, но рутинг — это опасно.
Неопытный пользователь из-за рутинга может испортить прошивку так, что смартфон больше не будет работать. Телефон с рутингом теряет гарантию и не обновляется производителем. Чтобы сделать рутинг, у вас должны быть очень веские основания и надежный компьютерщик под рукой.
Если на телефон с рутингом попадёт вредоносное приложение, то для его удаления придётся полностью перепрошивать телефон. Вредоносное приложение с рут-доступом сможет рассылать с вашего телефона сообщения на короткие номера и использовать банковские программы без вашего ведома.
Обзор особенностей ядра Андроида
“А я… карбюратор промываю!”
Анекдот
В детском садике мы с единомышленниками препарировали кузнечиков в надежде разобраться в их строении. В школе распаивали радиоприёмник “Россия”. В институте дошла очередь до автомобилей, гайки которых были многократно переставлены. Интересы поменялись, но желание “разбирать” иногда просыпается, и сегодня оно направлено на Андроид.
Сколько раз вас выручало наличие исходников Андроида? Меня — уже не счесть. Андроид — открытый проект, но, к сожалению, у нас есть возможность только читать; править код Андроида, не будучи сотрудником Google, практически невозможно. Погрустим над этим моментом и загрузим репозиторий. Как это сделать, отлично описано на официальном сайте.
Общая архитектура
Архитектуру Андроида можно схематично изобразить так:
Оригинальная схема не содержит информации об особенностях ядра и не акцентирует внимание на Binder-е и системных сервисах. А ведь Binder является “клеем”, связывающим все компоненты системы.
Как правило, в книгах описывается верхний левый синий прямоугольник, то есть API, которое доступно разработчику прикладных приложений. Нас же интересует всё, что ниже. Сегодня мы рассмотрим только ядро.
Ядро — центральная часть любого дистрибутива, называемого “Линукс”. Несмотря на доступность “чистого” ядра, многие разработчики (Ubuntu, Fedora, SuSe и т.д.) добавляют к нему свои патчи перед включением в дистрибутив. Андроид идёт той же дорогой, только ценой потери прямой совместимости: на “чистом” ядре он не заведётся. В настоящее время есть намерения включить “андроидизмы” в основную версию ядра, в 2011 году Линус Торвальдс давал на этот процесс 4-5 лет. Успех уже достигнут в рамках включения механизма wakelocks в версии ядра 3.5.
Рассмотрим “андроидизмы” более подробно.
История данного механизма эпична, потянет на сборник статей “Путь wakelock-ов в Линукс”: их обсуждение заняло порядка 2000 писем в рассылке LKML.
Настольные компьютеры и ноутбуки имеют устоявшуюся систему энергорежимов (у x86 процессоров таковых несколько): компьютер работает “на полных оборотах”, когда что-то делается, и уходит в энергоэффективный режим, когда система простаивает. Уход в “спящий” режим происходит либо после довольно длительного бездействия, либо вручную, например, при закрытии крышки ноутбука.
На телефонах требовался другой механизм: основное состояние системы — “спячка”, выход из него осуществляется только в случаях необходимости. Таким образом, система может уснуть, даже если какое-то приложение проявляет активность. В Андроиде был реализован механизм wakelock-ов: если приложение (или драйвер) выполняет что-то важное, что должно дойти до логического завершения, оно “захватывает” wakelock, предотвращая засыпание устройства.
Попытки портирования механизма wakelock-ов в ядро вызвали сопротивление многих разработчиков. Программисты Андроида решали конкретную проблему, решением которой стал определённый механизм. Условия задачи были весьма узки. Целевая платформа — ARM, поэтому использовались её особенности: ARM-процессоры изначально предполагают частую смену режимов работы “сна” и “бодрствования”, в отличие от x86. В Андроиде приложения общаются с системой управления питанием через PowerManager, а что делать клиентским Линукс-приложениям?
Разработчики Андроида даже не пытались найти общее решение “на будущее”, которое потом без проблем бы вливалось в основное ядро, не консультировались по этой проблеме с сообществом ядра Линукс. Можно ли их за это винить? Несмотря на все проблемы и обсуждения, как упоминалось выше, в ядре появилось API с идентичной функциональностью autosleep.
Программистам приложений под Андроид довольно редко приходится сталкиваться с wakelock-ами, так как платформа и драйверы обрабатывают возложенные на них обязательства с учётом “спящего” режима. Тем не менее, вмешаться в этот процесс поможет знакомый PowerManager. Кстати, автору приходит в голову только один сценарий: не дать телефону уснуть при запуске сервиса из BroadcastReceiver-а, что решается вспомогательным классом из Android Support Library WakefulBroadcastReceiver.
В стандартном ядре Линукса есть Out of Memory Killer, который на основании параметра badness определяет убиваемый процесс:
badness_for_task = total_vm_for_task / (sqrt(cpu_time_in_seconds) *
sqrt(sqrt(cpu_time_in_minutes)))
Таким образом, чем больше процесс потребляет памяти и чем меньше живёт, тем меньше ему повезёт.
Все программисты, читавшие документацию или проходившие собеседования, знают, что, во-первых, процесс может быть “убит” и при наличии свободных ресурсов, во-вторых, кандидат на вытеснение выбирается по другим критериям: наличие “живых” Андроид-компонент, видимость пользователю и так далее.
Массивы mOomAdj и mOomMinFreeLow/mOomMinFreeHigh как раз задают правила “когда что очистить”:
Таким образом, приложение домашнего экрана вытесняется при остатке свободной памяти в 73728 КБ на телефоне с экраном 1280×800 и ОЗУ в 700 МБ.
ProcessList передаёт соответствующие значения в ядро, что можно видеть в его методе updateOomLevels.
Приоритеты процессам выставляет Activity Manager Service, один из многих системных сервисов, общаться с которым можно через Activity Manager.
Binder, наряду с другими решениями (Files, Sigmals, Sockets, Pipes, Semaphores, Shared Memory и т.д.), решает задачу межпроцессного взаимодействия. Ноги у данного решения растут из проекта OpenBinder, разработчики которого в своё время перешли в команду Андроида.
Bionic (реализация libc) не использует System V IPC, так как в андроидовском окружении стандартные средства приведут к утечкам ресурсов.
Рассмотрим как это работает на примере LocationManager-а.
Как видим, за вызовом методов системных сервисов скрывается довольно большая логика.
Anonymous Shared Memory (ashmem) — механизм разделяемой памяти. В Линуксе, как правило, данный механизм реализован через POSIX SHM. Разработчики Андроида сочли его недостаточно защищённым, что могло сыграть на руку вредоносному ПО. Особенностями ashmem-а являются счётчик ссылок, при обнулении которого разделяемая память может быть освобождена (например, память освобождается при завершении всех процессов, использующих её), и сокращение разделяемого региона при нехватке памяти в системе.
Ярким примером использования ashmem-а является процесс zygote, в котором загружается стартовая версия Dalvik VM с загруженными базовыми классами и ресурсами, а остальные приложения просто ссылаются на эту память.
Binder имеет ограничение на размер транзакции в 1МБ (иначе будет выброшено исключение TransactionTooLargeException). Если нам надо передать из одного процесса в другой большой объём данных, можно как раз воспользоваться Ashmem-ом: создать MemoryFile и передать дескриптор файла в другой процесс.
Обычные дистрибутивы, как правило, используют две системы логирования: лог ядра, доступный через команду dmesg, и системные логи, располагающиеся обычно в директории /var/log.
Система Андроида включает несколько циклических буферов для хранения сообщений пользовательских программ (что продлевает время жизни карт памяти, так как циклы чтения-записи не расходуются впустую) и не имеет дополнительных задержек от работы с сокетами, которые применяются в стандартном syslog-е.
На диаграмме представлена общая система логирования Андроида. Драйвер логирования предоставляет доступ к каждому буферу через /dev/log/*. Приложения имеют доступ к ним не напрямую, а через библиотеку liblog. С библиотекой liblog общаются классы Log, Slog и EventLog. Команда adb logcat показывает содержимое буфера “main”.
В данной заметке мы кратко рассмотрели некоторые особенности Андроида как Линукс-системы. За скобками остались некоторые другие части (pmem, RAM console и т.д.), а также такие важные аспекты платформы в целом, как System Service, процесс запуска системы и другие. Если данная тема будет интересна, в следующих статьях мы рассмотрим и их.
Проблемы безопасности Android-приложений: классификация и анализ
Использование смартфонов в повседневной жизни не ограничивается голосовыми звонками и СМС. Возможность загружать и выполнять программы, а также мобильный доступ в Интернет привели к появлению громадного числа мобильных приложений. Функциональность современного смартфона составляют браузеры, клиентские программы социальных сетей, офисные приложения и всевозможные сервисы, работающие в Сети. Android-устройства заняли бóльшую часть рынка смартфонов за счет открытой архитектуры платформы Android и удобного API разработчика. На данный момент Android является наиболее популярной мобильной ОС с долей рынка более 75%. Количество приложений, загруженных из магазина Google Play, в 2016 году составило 65 миллиардов [1].
Параллельно наблюдается и бурный рост числа вредоносных приложений: в 2015 году их было обнаружено 2,3 миллиона [3]. Кроме того, около 60% Android-устройств работают на версиях ОС с известными уязвимостями, и они потенциально могут быть атакованы злоумышленниками [6]. Эти угрозы, в свою очередь, привели к развитию средств защиты. Официальный магазин Google Play ввел фильтрацию приложений с помощью песочницы Google Bouncer. Антивирусные компании стали выпускать свои продукты под Android (хотя, как показано в [8], многие из них сами по себе содержат опасные уязвимости). Число научных публикаций по данной теме также возросло: одна из обзорных работ 2015 года о решениях безопасности для Android [2] насчитывает более 40 проектов и упоминает далеко не все известные на данный момент исследования. В связи с тем, что мобильные устройства являются источником и хранилищем большого количества конфиденциальных данных, проблема безопасности ОС Android и ее приложений стоит особо остро.
Платформа Android является свободным ПО, база ее исходных кодов полностью открыта. Производители устройств самостоятельно развивают кодовую базу, создавая специализированные прошивки с целью достижения большей функциональности и лучшей производительности. Побочным результатом такой деятельности становятся уязвимости и слабости в реализации алгоритмов, отсутствующие в основной кодовой базе, но существующие на множестве реальных устройств. Вредоносное ПО использует эти уязвимости для повышения прав и преодоления защитных механизмов. Выявление уязвимостей в прошивках при отсутствии исходных кодов крайне затруднено. Первоочередной проблемой становится отсутствие среды контролируемого выполнения, которая необходима для проведения динамического анализа.
Таким образом, полноценный анализ безопасности устройства требует изучения свойств системного и прикладного ПО в совокупности. В данной статье представлена собственная классификация проблем безопасности Android, а также список требований к системе полносистемного анализа платформы Android.
1. Устройство ОС Android
В основе ОС Android лежит ядро Linux с некоторыми архитектурными изменениями, которые были сделаны инженерами Google. Приложения для операционной системы Android разрабатываются на языке Java. Начиная с версии Android 1.5 был представлен набор инструментов Android NDK, который позволяет разрабатывать модули приложений на языках С и С++ и компилировать их в машинный код [4]. Приложения поставляются в виде файлов специального формата APK, который является ZIP-архивом с определенной структурой каталогов и файлов. APK-файл приложения содержит:
Среда выполнения Java-программ Dalvik VM на Android сильно отличается от «обычной» Java VM. Во-первых, при компиляции Java-программы она компилируется в байт-код виртуальной машины Dalvik, который сильно отличается от байт-кода виртуальной машины HotSpot. Виртуальная машина Dalvik является регистровой, что делает ее выполнение на RISC-архитектурах, часто используемых в мобильных устройствах, более эффективным. Также при разработке Dalvik учитывались ограничения памяти в мобильных устройствах. Начиная с версии Android 2.2 среда выполнения Dalvik содержит JIT-компилятор, который компилирует «горячие» куски кода Java-программ в машинный код [5].
Стандартные библиотеки Java были либо заменены на доработанные библиотеки из пакета Apache Harmony либо написаны заново. При компиляции Java-программы получается файл формата DEX (Dalvik Executable), который содержит байт-код для виртуальной машины Dalvik. Формат файла был разработан с целью сокращения объема занимаемой памяти и существенно отличается от стандартного формата JAR. Для вызова модулей, реализованных в машинном коде, из Java-программ используется интерфейс JNI. Стоит отметить, что имеется возможность подгружать машинные модули динамически по сети с помощью компонента DexClassLoader.
2. Проблемы безопасности
Родительским процессом для всех приложений в ОС Android является процесс Zygote. Данный процесс представляет собой каркас Android-приложения, в котором уже загружены все необходимые библиотеки окружения Android, но отсутствует код самого приложения. Запуск приложения Android с точки зрения операционной системы происходит следующим образом:
Рис. 1. Запуск нового приложения в ОС Android
По умолчанию приложение ограничено в своих возможностях и не может сделать ничего, чтобы негативно повлиять на другое приложение или пользователя: ни прочитать пользовательские данные, ни модифицировать системные приложения; отсутствует доступ к сети. Для получения доступа к различным ресурсам приложение обращается к сервисам ОС Android. Разрешения на доступ задаются статически в файле манифеста приложения и выдаются приложению во время его работы по мере необходимости. Система запрашивает у пользователя согласие на выдачу этих разрешений во время установки или во время обновления приложения. Ответственность за выдачу доступа приложению лежит на пользователе, он самостоятельно решает, какому приложению давать разрешения на определенные действия, а какому не давать, — во время его установки. Использование такого подхода, при котором все разрешения выдаются разом при установке приложения, привело к тому, что пользователи стали раздавать полномочия приложениям, не задумываясь о последствиях. В свою очередь, приложения стали запрашивать все больше разрешений по мере расширения их функциональности. В Android 6 Marshmallow данная система заменена на другую: приложение запрашивает доступ к ресурсам у пользователя во время его работы, а пользователь может либо разрешить доступ, либо запретить его [11].
Android-приложение состоит из четырех видов компонентов и не содержит функции main() или какой-то другой единой точки входа. Компоненты приложений взаимодействуют друг с другом с помощью специальных сообщений, называемых Intent.
Компоненты под названием Activity определяют пользовательский интерфейс. Как правило, используется один компонент Activity для описания одного экрана приложения. Activity может запустить другое Activity, передав параметры с помощью механизма Intent. Во время работы только одно Activity может работать и обрабатывать пользовательский ввод, остальные на это время остаются замороженными или уничтожаются, в зависимости от выбранного режима работы приложения.
Компоненты под названием Service [9] производят фоновую обработку. Когда Activity нужно выполнить какую-то операцию, например загрузку файла или проигрывание музыки, и продолжить работу, когда пользователь перешел в другое приложение или свернул текущее, приложение запускает сервис, цель которого — выполнение этой операции. Разработчики могут использовать Service в качестве приложения-демона, стартующего во время загрузки системы. Компонент Service, как правило, поддерживает RPC (Remote Procedure Call), поэтому другие компоненты системы могут обращаться к нему.
Компонент Content provider сохраняет и обменивается данными, используя интерфейс реляционных баз данных. Каждый Content provider содержит уникальный URI для данных и обрабатывает запросы к нему в виде очередей SQL (Select, Insert, Delete).
Компоненты Broadcast receiver выступают в роли ящиков для сообщений от других приложений.
Проблемы безопасности, возникающие в ОС Android, рассматривались в работах [2, 12, 13], но классификация проблем по категориям дана только в статье [2]. Эта классификация достаточно подробная и охватывает многие проблемы безопасности, но у нее есть ряд существенных недостатков: некоторые категории охватывают широкую область уязвимостей, в то время как другие достаточно специализированы; приведенные категории уязвимостей никак не соотносятся с программными уровнями архитектуры ОС Android; не охвачены уязвимости из интернет-источников, а также слабо охвачены уязвимости в протоколах и аппаратуре (п. 2.7 и 2.8 в нашей классификации). Предлагаемая ниже классификация уязвимостей устраняет эти недочеты.
2.1. Уязвимости ядра Linux и его модулей
К данной категории проблем относятся уязвимости, которые присущи всем ОС, основанным на той же версии ядра Linux, что и ОС Android. Эксплойты, использующие уязвимости в ядре, могут получить данные пользователя или права администратора системы. Повысив привилегии процесса до прав администратора системы, вредоносная программа может отключить систему безопасности Android, вставить в существующие программы вредоносный код и установить руткит. К тому же производители различных устройств добавляют в ядро модули для своих устройств; в этих модулях также могут быть уязвимости. Примеры уязвимостей повышения привилегий описаны в [14, 15, 16, 64]. Также стоит отметить, что совсем недавно была обнаружена уязвимость в компоненте ashmem для межпроцессного взаимодействия в Android [62].
2.2. Уязвимости модификаций и компонентов производителей устройств
В последнее время производители различных мобильных устройств стали выпускать свои модифицированные прошивки Android. Эти прошивки могут содержать различные приложения и сервисы, разработанные производителем устройства, которые чаще всего нельзя удалить. Например, в октябре 2016 года был обнаружен скрытый бэкдор в прошивках Foxconn [63]. Анализ этих сервисов, приведенный в статьях [17], показывает, что в них содержится от 65 до 85% уязвимостей, обнаруженных во всей системе. К тому же стоит отметить, что уязвимости, которые были обнаружены и исправлены в основной ветке Android, могут долгое время оставаться в ветках производителей устройств [18, 19].
2.3. Уязвимости модулей в машинных кодах
Android-приложения поддерживают запуск машинного кода через интерфейс JNI. Это порождает еще одну категорию уязвимостей, связанную с широко известными уязвимостями утечек памяти в низкоуровневых языках (например, в С и С++ ) [20]. Поскольку на уровне процессов ОС Android нет никаких ограничений, кроме накладываемых ядром Linux, сторонние библиотеки в машинных кодах могут использовать разрешения, выданные всему приложению, для совершения вредоносной активности (см. также следующую категорию уязвимостей, п. 2.4). Также модули приложения в машинных кодах используются авторами вредоносных приложений, чтобы обойти инструменты анализа и мониторинга уровня Android. Эти модули также могут использовать техники противодействия анализу, используемые в обычных программах.
2.4. Уязвимости механизмов межкомпонентного взаимодействия
К данной категории относятся уязвимости, связанные с взаимодействием между различными компонентами приложений. Так как на уровне операционной системы приложение ограничено песочницей процесса, ему необходим механизм доступа к компонентам ОС Android для взаимодействия с ними. Это происходит через устройство /dev/Binder и различные другие сервисы ОС Android. Как уже говорилось выше, параметры этого доступа задаются в файле манифеста, в виде XML-файла с разрешениями. Анализ, приведенный в статьях [12, 21, 22, 23, 24, 25], указывает на недочеты этой системы ограничений и показывает пути их обхода. Так, например, приложение может воспользоваться правами доступа другого приложения и получить с помощью него данные через ICC. Также могут быть уязвимости, связанные со сторонними библиотеками. Сторонние библиотеки, используемые в приложении, получают тот же набор ограничений, что и само приложение. Поэтому сторонние библиотеки могут использовать разрешения, выданные всему приложению, для совершения вредоносной активности. Приложения к тому же могут перехватывать системные события, пересылаемые через широковещательный запрос, и сохранять информацию о входящих звонках и СМС.
2.5. Уязвимости в самих приложениях
Каждое приложение сохраняет какие-то данные о пользователе. Эти данные должны быть защищены должным образом, чтобы к ним не могли получить доступ другие приложения, — но такая защита предусмотрена не всегда. Например, Skype в одной из версий приложения сохранял базу данных контактов в открытом виде на диске. Таким образом, контакты можно было прочитать любым другим приложением, у которого есть доступ к диску [26]. Также приложения могут использовать криптографические библиотеки с ошибками [27] или же какие-то собственные реализации. К тому же не все приложения производят хорошую аутентификацию и авторизацию пользователя. Кроме этого, приложения могут позволять SQL-инъекции и подвержены атакам XSS. Также стоит отметить, что большинство разрабатываемых приложений написаны на Java без использования какой-либо защиты для бинарного кода, а байт-код Java, как известно, легко поддается дизассемблированию и анализу. Стоит отметить, что к этой категории уязвимостей относится также известный список Mobile OWASP-10. Многие подобные уязвимости описаны в [28, 29].
2.6. Уязвимости во встроенных сервисах и библиотеках
Стандартный набор библиотек и сервисов, работающих в Android, также содержит уязвимости. Например, недавно была обнаружена уязвимость Stagefright в библиотеке для отображения видео в MMS-сообщениях, которой были подвержены все версии Android, начиная с 2.2 [30]. Позже была обнаружена уязвимость в компоненте MediaServer, которой подвержены все версии Android c 2.3 до 5.1 [31]. В статье [13] показаны уязвимости рантайма Dalvik: запустив большое количество процессов в системе, можно добиться, что последующий процесс запустится с правами администратора.
2.7. Интернет-источники
Android-приложения распространяются через широкое количество источников помимо официального магазина приложений. Поскольку Android-приложения написаны в основном на Java, то они легко поддаются обратной разработке и переупаковке с использованием вредоносного кода [32, 33]. Кроме того, как было показано в статье [34], песочницу анализа приложений Bouncer, используемую в официальном каталоге, легко обойти. Поэтому и в самом официальном магазине содержится большое количество вредоносных программ. Кроме этого, Android поддерживает удаленную установку приложений через Google Play на устройства, связанные с Google-аккаунтом. Таким образом, если взломать учетную запись Google для устройства, можно установить из Google Play вредоносное приложение, которое туда предварительно загрузил злоумышленник. При этом на экране мобильного телефона не требуется каких-либо подтверждений этих действий, поскольку они запрашиваются в окне браузера и приложение устанавливается на телефон в фоновом режиме при получении доступа к Интернету. Также к этой категории уязвимостей относится использование социальной инженерии, когда для продолжения работы предлагают установить приложение из неавторизованного источника [35].
2.8. Уязвимости аппаратуры и связанных с ней модулей и протоколов
Мобильные устройства, работающие под управлением ОС Android, имеют широкий набор аппаратуры для взаимодействия с внешним миром. Соответствующие уязвимости можно эксплуатировать при непосредственной близости к устройству или при наличии физического доступа к устройству.
Примерами таких атак служат атака типа «отказ в обслуживании» на технологию Wi-Fi Direct [36], кража данных кредитных карт с помощью NFC [37], исполнение удаленного кода через Bluetooth [38], установка вредоносного приложения без ведома пользователя через adb с помощью механизма бэкапов [39]. В работе [13] показаны уязвимости, с помощью которых можно повысить привилегии приложения, используя ошибки в реализации протокола adb.
3. Инструменты для анализа Android-приложений
С того момента, как были выпущены первые Android-телефоны, было написано большое количество инструментов для анализа Android-приложений. Наиболее полный обзор этих инструментов содержится в статьях [40, 41, 42, 2]. В первых трех источниках инструменты классифицированы по видам анализа: статический, динамический и их объединение (смешанный). В статье [2] используется классификация инструментов по стадиям развертывания приложения на Android-устройстве. В нашей работе используется классификация по видам анализа.
Статический анализ
Инструменты статического анализа можно разделить на следующие категории:
Radare2 [46] — инструмент с открытым исходным кодом для дизассемблирования, анализа, отладки и изменения бинарных файлов Android-приложения.
Один из самых разносторонних инструментов для статического анализа — Androguard [47]. Он может дизассемблировать и декомпилировать приложение обратно в исходный код Java. Получив два APK-файла, он может посчитать коэффициент их похожести для детектирования переупакованных приложений или известных вредоносных приложений. Благодаря своей гибкости он используется в некоторых инструментах смешанного анализа.
Более полный список инструментов статического анализа можно найти в статьях, указанных выше. Следует отметить, что статический анализ имеет ряд существенных ограничений, связанных с тем, что имеется лишь абстрактное представление о программе. Например, статический анализ становится намного сложнее, если к программе применены обфусцирующие преобразования. В зависимости от сложности обфускации некоторые (а может, и все) статические подходы могут стать абсолютно бесполезными. Если вызов каждого метода делается только косвенно, вряд ли удастся построить граф вызовов программы. В Android такое преобразование можно сделать, используя Java Reflection API для вызова методов и создания объектов вместо использования обычных вызовов и инструкций создания нового объекта. На рынке решений по защите исходного кода уже представлено несколько продуктов для обфускации файлов Android-приложений, которые могут свести на нет все преимущества статического анализа [48, 49]. Более подробно о техниках противодействия статическому анализу можно почитать в 50.
Динамические и смешанные инструменты анализа
Инструменты динамического анализа отслеживают поведение неизвестного приложения во время выполнения при запуске его в контролируемой песочнице для получения поведенческого следа. Для этого производится инструментирование песочницы на различных уровнях архитектуры участками кода, которые отслеживают поведение приложения и ОС Android.
Рис. 2. Уровни архитектуры песочницы Android
Архитектура песочницы Android представляет из себя эмулятор Android (чаще всего QEMU), внутри которого работает ОС Android. Инструментирование производится либо на уровне эмулятора, либо на уровне ОС Android, либо и там и там. Уровень ОС Android также делится на четыре подуровня:
4. Идеальная система полносистемного анализа платформы Android
Статьи [40, 41, 42, 2] насчитывают более 40 различных инструментов как для анализа Android-приложений, так и для анализа ОС Android в целом. Как было замечено в статьях [34, 60, 61], существующие инструменты динамического анализа обладают рядом серьезных недостатков. Данные недостатки присущи и стандартному инструменту анализа приложений в магазине Google Play — Google Bouncer, вследствие чего вредоносные приложения могут распространяться через официальный магазин, не будучи обнаруженными.
Сопоставление возможностей подходов и систем в рассмотренных публикациях позволяет сформулировать требования к идеальной системе анализа платформы Android, которая способна анализировать в совокупности приложения и все слои системного ПО. Система заимствует некоторые эффективные приемы из рассмотренных работ, комбинируя их с целью преодоления недостатков в существующих решениях.
Архитектура системы представлена на рис. 3. Основным компонентом является полносистемный эмулятор, который может загружать как прошивки ОС Android с реальных устройств, так и официальный образ Android для эмулятора. Эмулятор поддерживает проброс датчиков от реального устройства, а также исполнение отдельных участков кода на реальных устройствах. Внутри эмулятора работают модули, которые обеспечивают:
1. Поддержка загрузки прошивок от производителей различных устройств
Существенным недостатком всех существующих инструментов анализа ОС Android и ее приложений является невозможность запуска прошивок от производителей устройств в доступных эмуляторах. Как было показано в п. 2.2, модифицированные прошивки от производителей устройств содержат от 65 до 85% уязвимостей, обнаруженных во всей системе. На данный момент не существует инструментов для анализа, которые позволяли бы запускать прошивки от производителей. Все существующие решения работают на специальной сборке Android для виртуальной платформы GoldFish.
2. Возможность исполнения отдельных кусков машинного кода на реальном устройстве
По информации из статей [34, 61], существуют способы выявления работы внутри эмулятора. Как правило, выявляется выполнение в эмуляторе QEMU в режиме бинарной трансляции, поскольку именно на нем основано большинство песочниц. Способы основаны на разном поведении определенных участков машинного кода в эмуляторе и на реальном устройстве. Так как изменение механизма бинарной трансляции представляется сложной задачей, исполнение отдельных кусков машинного кода на реальном устройстве было бы хорошим подходом для противостояния данным способам обнаружения эмулятора.
3. Проброс данных от датчиков оборудования реальных устройств в эмулятор
Как было описано в статьях [34, 61], существуют способы определения эмулятора, основанные на данных, получаемых от датчиков оборудования, таких как акселерометр, гироскоп, GPS, датчик света, силы тяжести. Выходные значения этих датчиков основаны на информации, собранной из окружающей среды, и следовательно, реалистичная их эмуляция является сложной задачей. Присутствие такого рода датчиков — основное различие между смартфонами и настольными компьютерами. Увеличивающееся число датчиков на смартфонах дает новые возможности для идентификации мобильных устройств.
4. Совместный анализ на уровне Java-кода и машинного кода
Затруднением для многих систем анализа Android-приложений является то, что приложения содержат как модули в байт-коде Dalvik, так и модули в машинных кодах. Из существующих решений поддержка анализа всех модулей реализована только в DroidScope [57] и CopperDroid [58, 59]. Идеальная система анализа должна позволять отслеживать потоки данных и управления на уровне пользовательского и системного кода. Для пользовательского кода, когда это возможно, следует поднимать уровень представления до Java-кода, являющегося высокоуровневым языком разработки. Также необходимо обеспечивать «склейку» потоков данных и управления при переходе от машинного кода к Java и наоборот.
5. Поддержка анализа межкомпонентного взаимодействия
В статье о CopperDroid [58] показана реализация поддержки анализа межкомпонентного взаимодействия как внутри Android-приложения, так и между различными приложениями. Это сделано с помощью перехвата данных, проходящих через компонент ядра Binder, так как все взаимодействие проходит через него. Подход, реализованный в CopperDroid, позволяет не производить модификации исходного кода Android, что делает возможность его переноса на новые версии ОС Android и новую среду запуска приложений ART с минимальными изменениями.
6. Защита от статических эвристик обнаружения
Как показано в статьях [[57], 61], большинство песочниц для анализа можно обнаружить, просто проверив значения IMEI, IMSI или номера сборки прошивки у устройства. Также эмулятор может быть обнаружен, если проверить на стандартные значения таблицу маршрутизации. Из существующих решений защита от обнаружения по статическим эвристикам реализована только в проекте ApkAnalyzer [65].
7. Минимальные изменения прошивок Android
Также стоит отметить, что многие решения динамического анализа основаны на инструментировании кода различных компонентов ОС Android, в частности виртуальной машины Dalvik. Это осложняет их дальнейшую поддержку, а также миграцию в новую среду запуска приложений ART. Многие песочницы ограничиваются только анализом кода компонентов Java, тогда как все больше и больше приложений используют компоненты в машинных кодах.
8. Поддержка полносистемного анализа помеченных данных с отслеживанием потоков управления
Стоит отметить, что многие из инструментов динамического анализа используют анализ помеченных данных, используя для этого инструмент TaintDroid [56]. В статье [60] были показаны успешные способы обхода анализа данного инструмента. Причиной успешности данных атак являются следующие факты: 1) TaintDroid отслеживает только потоки данных и не отслеживает потоки управления, 2) TaintDroid отслеживает потоки данных только на уровне виртуальной машины Dalvik и проходящих через интерфейс JNI. Возможные пути разрешения данных недостатков описаны в [60] и дают направление для дальнейших исследований.
9. Поддержка символьного исполнения и частичного исполнения с конкретными значениями с использованием данных, полученных из статического анализа кода (concolic execution — смешанное исполнение)
В статье [51] описана среда анализа, реализующая данный подход. Эта среда сочетает в себе техники статического анализа графа потока управления программы, символьного исполнения программы и исполнения программы с конкретными значениями. Подходы, сочетающие техники символьного исполнения и исполнения программы с конкретными значениями, описаны в статьях [52, 53, 54, 55]. Целью данного метода является наблюдение путей исполнения, которые приводят к секциям программы, содержащим «интересный» код. Под «интересным» кодом понимают такой код, выполнение которого зависит от каких-либо произошедших внешних событий или настроек окружения системы. Например, код классов в Android, динамически подгружаемый с помощью компонента DexClassLoader или вызов «машинных» методов через интерфейс JNI.
Заключение
Проблемы безопасности мобильной ОС Android существуют на всех уровнях платформы и требуют более комплексного подхода, чем те меры защиты, которые можно наблюдать сейчас. При разработке классификации уязвимостей, представленной в данной статье, было замечено, что одна из главных проблем — значительная фрагментация рынка, которая не позволяет организовать своевременную поставку обновлений безопасности всем устройствам, как это реализовано в iOS.
При этом ныне существующие средства защиты, включая антивирусы и песочницы, имеют множество недостатков и не могут полностью защитить пользователя. Хотя создать песочницу для полносистемного анализа ОС Android без описанных недостатков можно, если руководствоваться списком требований, представленных в данной статье.
Несмотря на такую пессимистичную картину, в последнее время наблюдается ряд позитивных движений в сторону улучшения безопасности Android. В частности, компания Google запустила программу выпуска обновлений безопасности: они выходят каждый месяц и некоторые вендоры все же добавляют их в свои версии прошивки (устройства, поддерживаемые Google, получают эти обновления первыми). Кроме того, пользователи могут самостоятельно установить на свои устройства версию ОС Android CyanogenMod (ныне LineageOS), которая поддерживает эти обновления безопасности. Также пользователь может снизить риски, если ограничится набором популярных приложений только из официального магазина Google Play. Уязвимости типа RCE (удаленное выполнение кода) встречаются все реже, поэтому доставка вредоносных приложений на телефоны чаще происходит через фишинговые сайты, неофициальные магазины приложений или с помощью социальной инженерии. Среднестатистический пользователь ОС Android, соблюдая определенную технику безопасности, может быть спокоен за свой смартфон.