Сегодня в выпуске: улучшения безопасности Android Q, обновления Android через Google Play, разбор DoS-эксплоита против почти всех смартфонов Samsung, исследование северокорейского клона игры Sim City, программирование интерфейса приложения без XML и головной боли, верификация целостности приложения, а также очередная подборка библиотек для программистов.
 

Почитать

 

Улучшения безопасности Android Q

Queue the Hardening Enhancements, What’s New in Android Q Security — две статьи инженеров Google о сделанных в Android Q улучшениях безопасности. Основные моменты:

  • Adiantum. С выпуском Android 7 Google ввела требование принудительного включения шифрования на всех новых устройствах с аппаратной поддержкой шифрования AES. Теперь благодаря разработанному в Google механизму шифрования Adiantum это требование распространяется и на все остальные смартфоны и планшеты. Реализация Adiantum базируется на применении быстрой хеш-функции NH, алгоритме аутентификации сообщений (MAC) Poly1305 и потоковом шифре XChaCha12, а также единоразовой операции на базе блочного шифра AES-256 для 16 байт в каждом блоке. В тестах на процессоре ARM Cortex-A7 Adiantum показывает пятикратное превосходство в скорости шифрования над AES-256-XTS.
  • Изоляция медиакодеков. В Android 7 Google вынесла медиасервер, отвечающий за обработку мультимедийных файлов, в изолированную песочницу с низкими правами доступа. В Android 8, в рамках проекта Treble, медиасервер был разделен на два компонента, работающих в изолированных песочницах. Однако набор софтверных кодеков оставался сплавлен с хардварными кодеками, которые, в свою очередь, имели доступ к драйверам. Все это приводило к тому, что баг в софтверном кодеке мог использоваться для получения контроля над драйвером медиаускорителя, и наоборот. В Android Q софтверные кодеки переехали в свою собственную песочницу.
  • Проверки на переполнение. Начиная с Android 7 инженеры Google постепенно внедряли функции проверки границ массивов и проверки на целочисленное переполнение с помощью компилятора LLVM (функции BoundSan и IntSan). В Android Q проверками удалось покрыть одиннадцать медиакодеков и стек Bluetooth. По словам разработчиков, уже существовавшие в Android 9 проверки позволили нейтрализовать одиннадцать различных уязвимостей.
  • Control Flow Integrity. В Android Q продолжили работать над внедрением функции CFI, которая строит граф вызовов функций и завершает процесс при выходе за его пределы. CFI позволяет предотвратить (или существенно усложнить) атаки, основанные на использовании ROP (Return Oriented Programming), когда эксплоит использует фрагменты кода уязвимого процесса для выполнения нужного действия (например, открытия шелла).
  • eXecute-Only Memory. В Android Q, работающем на платформе с процессорной архитектурой AArch64, код всех библиотек и бинарных файлов помечен как «только для исполнения». Так же как CFI, это делает атаки с использованием ROP более проблематичными, ведь атакующему теперь необходимо не только получить возможность передать управление нужному фрагменту кода, но и найти способ прочитать этот фрагмент.
  • Аллокатор Scudo. Для мультимедиакодеков Android Q теперь использует аллокатор памяти Scudo, затрудняющий атаки типа use-after-free, double-free и переполнение буфера.
Прогресс изоляции медиакодеков: от Android M до Q
Прогресс изоляции медиакодеков: от Android M до Q
 

Модульные обновления Android

Fresher OS with Projects Treble and Mainline — подробности проекта Mainline, позволяющего обновлять отдельные системные компоненты Android без обновления платформы целиком.

Обновления будут распространяться в специальном формате пакетов APEX, который вместо приложения содержит в себе определенный компонент системы. В данный момент таким компонентом может быть: мультимедийный кодек, мультимедийный фреймворк, DNS-резолвер, Conscrypt Java Security Provider, Documents UI, Permission Controller, ExtServices, данные часовых поясов, ANGLE (прослойка для трансляции вызовов OpenGL ES в OpenGL, Direct3D 9/11, Desktop GL и Vulkan), Module Metadata, сетевые компоненты, Captive Portal Login и настройки сетевого доступа.

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

Содержимое пакета APEX
Содержимое пакета APEX

Интересно, что APEX не производит обновление «на живую», когда старый компонент заменяется на новый. Раздел /system в Android недоступен для записи, поэтому APEX использует трюк с монтированием. Все файлы внутри пакета APEX находятся в образе файловой системы ext4. Когда происходит «установка» пакета, система монтирует этот образ поверх раздела /system в режиме bind. В результате файлы пакета как бы заменяют оригинальные файлы Android, хотя в реальности все остается на своих местах. Точно такой же трюк использует Magisk для установки модификаций Android без изменения раздела /system.

 

Как «окирпичить» любой смартфон Samsung

How to brick all Samsung phones — статья известного специалиста по IT-безопасности Эллиота Алдерсона (Elliot Alderson) о баге смартфонов Samsung, позволяющем стороннему разработчику залочить смартфон так, что им невозможно будет пользоваться.

Проблема заключается в приложении ContainerAgent, которое имеет ресивер SwitcherBroadcastReceiver, принимающий среди прочих интент com.samsung.android.knox.containeragent.LocalCommandReceiver.ACTION_COMMAND.

Объявление ресивера в манифесте
Объявление ресивера в манифесте

Декомпилировав код ресивера, можно выяснить, что при обработке интента проверяются значения com.samsung.android.knox.containeragent.LocalCommandReceiver.EXTRA_COMMAND_ID и android.intent.extra.user_handle. Если передать в первом 1001, а во втором 150 (идентификатор пользователя Knox User, который отвечает за защищенное хранилище данных), контейнер будет немедленно закрыт:

$ adb shell am broadcast -a com.samsung.android.knox.containeragent.LocalCommandReceiver.ACTION_COMMAND --ei "com.samsung.android.knox.containeragent.LocalCommandReceiver.EXTRA_COMMAND_ID" 1001 --ei "android.intent.extra.user_handle" 150

Если же передать 1001 и 0, произойдет немедленный переход на домашний экран:

$ adb shell am broadcast -a com.samsung.android.knox.containeragent.LocalCommandReceiver.ACTION_COMMAND --ei "com.samsung.android.knox.containeragent.LocalCommandReceiver.EXTRA_COMMAND_ID" 1002 --ei "android.intent.extra.user_handle" 0

Чтобы показать, как это можно использовать, Эллиот создал приложение, которое каждую секунду отправляет два этих интента и таким образом блокирует любые попытки использовать смартфон. Пользователь постоянно возвращается на домашний экран, а его защищенная папка, которую Samsung призывает использовать для хранения действительно важных данных, постоянно блокируется.

 

Исследование северокорейского Sim City

Reverse Engineering a North Korean Sim City Game — статья о северокорейском клоне игры Sim City, в котором внутриигровые покупки совершаются в офлайне. Интересные моменты:

  • В Северной Корее нет онлайновых магазинов приложений, но есть множество офлайновых. Покупатель приходит в магазин со своим телефоном, выбирает приложение или игру, платит за нее (в случае с данной игрой — менее одного доллара), затем отдает телефон продавцу, и тот заливает приложение на телефон (иногда только с десятой попытки). Примерно таким же образом совершаются внутриигровые покупки (лут, игровая валюта, персонажи и прочее).
  • Купленная игра City Management оказалась не внутренней разработкой, а всего лишь клоном китайской версии игры City Island 2, разработанной в Нидерландах.
  • Сразу после запуска игра выводит на экран числовой код, который необходимо предоставить продавцу приложения, чтобы получить лицензионный ключ. Механизм сверки ключей использует реализацию алгоритма RSA двадцатилетней давности (RSAREF).
  • Обойти защиту очень просто: достаточно модифицировать функцию проверки лицензии так, чтобы она всегда возвращала положительный результат, и перепаковать приложение. Однако в этом случае игра завершится с ошибкой, потому что в коде есть проверка на целостность библиотек, используемых для проверки лицензии. К счастью, обращение к этой функции можно просто удалить из кода.
  • Код проверки лицензии и целостности файлов располагается внутри «безобидных» функций, внутри самого движка игры, поэтому найти их, используя статистический анализ, практически невозможно.
  • Модифицированная версия игры не запустится на устройстве, предназначенном для внутреннего рынка Северной Кореи. Такие смартфоны и планшеты представляют собой ребрендированный дешевый китайский ноунейм с установленной специальной версией Android, которая сверяет цифровые подписи почти всех типов файлов (документов, изображений, фильмов, приложений) и либо не позволяет их открыть, либо удаляет.
  • При нажатии на кнопку приобретения внутриигровой валюты появляется сообщение с номером заявки и предложение ввести другой номер. Его можно получить все в том же офлайновом магазине приложений.
Вывески офлайновых магазинов приложений в Северной Корее
Вывески офлайновых магазинов приложений в Северной Корее
 

Разработчику

 

Погружение в Jetpack Compose

Diving into Jetpack Compose — статья о новой библиотеке Jetpack Compose, представленной на Google I/O 2019. Compose позволяет описывать UI прямо в коде в декларативном стиле без необходимости править XML.

Пример приложения, написанного с использованием Compose:

class ComposeActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { CraneWrapper { MyApp() } }
    }

    @Composable
    fun MyApp() {
        MaterialTheme {
            Text(text = "Hello world!", style = +themeTextStyle { h3 })
        }
    }
}

Этот код создает активность в стиле Material Design с текстом «Hello World!». Функция CraneWrapper необходима для настройки провайдеров Context, FocusManager и TextInputService. Функция MaterialTheme настраивает цвета, стили и шрифты в соответствии с правилами Material Design.

Compose — это реактивный UI-фреймворк, существенно упрощающий работу с состояниями виджетов:

@Composable
fun MyApp() {
    MaterialTheme { Counter() }
}

@Composable
fun Counter() {
    val amount = +state { 0 }

    Column {
        Text(text = "Counter demo")
        Button(text = "Add", onClick = { amount.value++ })
        Button(text = "Subtract", onClick = { amount.value-- })
        Text(text = "Clicks: ${amount.value}")
    }
}

Данный код создает экран с кнопками Add и Substract и текстом Text: <число>. Нажатие любой из кнопок не только увеличивает внутреннее значение числа amount.value, но и приводит к немедленному обновлению текстового виджета.

В качестве значения в коде +state { } можно использовать не только числа и другие простые типы данных, можно создать и собственную модель данных:

@Model
class CounterModel {
    var counter: Int = 0
    var header = "Counter demo"
    fun add() { counter++ }
    fun subtract() { counter-- }
}

Но самая интересная особенность Compose в том, что внутри он не использует стандартные виджеты и компоненты Android (View, Fragment и прочие). Вместо этого он рисует все виджеты самостоятельно, так же как это делает Flutter. Такая архитектура позволяет полностью абстрагироваться от версии Android и изменений в интерфейсе, внесенных производителями смартфонов, и получить полностью идентичный UI на любом устройстве (библиотеки поддержки Android это тоже могут, но ценой жутких костылей и необходимости допиливать вручную).

Отказ от использования стандартных виджетов Android также позволил Google создать более гибкий и современный фреймворк, многие из идей которого позаимствованы опять же из Flutter. Например, одна из ключевых идей Compose — «все есть виджет». Даже отступы для других виджетов реализуются с помощью виджета:

Padding(padding = 16.dp) {
    Button(text = "Say hello", onClick = { ... })
}

Это гораздо более интуитивный и понятный способ описания интерфейса.

Также одно из важных следствий описания интерфейса прямо в коде — возможность использовать любые языковые конструкции Kotlin:

Column {
    listOf("John", "Julia", "Alice", "Mark").forEach {
        Text(text = it)
    }
}
 

Верифицируем целостность приложения

Verify non-Google Play app installs — статья из официальной документации Google, посвященная решению проблемы, с которой столкнулись многие разработчики приложений после перехода на формат пакетов App Bundle.

Напомним, что App Bundle — это специальный формат пакета для приложений, который позволяет упаковать в один файл все возможные ресурсы и библиотеки приложения. При публикации релиза разработчик заливает App Bundle в Google Play, а тот, в свою очередь, «разрезает» бандл на множество независимых файлов APK, содержащих ресурсы и библиотеки для разных устройств. Например, файлы перевода попадают в собственные независимые пакеты, и эти пакеты устанавливаются отдельно от основного пакета приложения в зависимости от того, какой язык выбран в качестве дефолтового на устройстве пользователя.

App Bundle позволяет сократить размер приложения, не жертвуя функциональностью и не обременяя разработчика, которому иначе пришлось бы самостоятельно создавать и заливать в Google Play множество разных APK для разных платформ и устройств. Но есть одна проблема: если пользователь скачает приложение не напрямую из Google Play (например, используя специальные даунлоадеры, альтернативные маркеты, или просто достанет приложение из другого телефона), приложение может начать сбоить из-за недостающих конкретно для этого устройства файлов APK.

К счастью, у Google есть инструмент, который позволяет проверить, все ли необходимые компоненты приложения установлены на устройство. Чтобы им воспользоваться, достаточно выполнить три простых шага:
 1. Добавить следующую строку в основной build.gradle приложения:

buildscript {
    dependencies {
        ...
        classpath 'com.android.tools.build:bundletool:0.9.0'
    }
}

 2. Добавить в зависимости библиотеку Play Core:

dependencies {
    ...
    implementation 'com.google.android.play:core:1.6.0'
}

 3.1. Изменить манифест приложения:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication" >
    <application
        ...
        android:name="com.google.android.play.core.missingsplits.MissingSplitsDetectingApplication" >
    </application>
    ...
</manifest>

 3.2. Либо, если приложение использует кастомный класс Application, сделать так:

public class MyCustomApplication extends Application {
    @Override
    public void onCreate() {

        if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
            // Skip app initialization
            return;
        }

        super.onCreate();
        ...
    }
}

Теперь пользователи, скачавшие приложение в обход Play Store, увидят такое сообщение:


 

Стоит ли обертывать сторонние библиотеки враппером?

Bohemian Wrapsody — статья о достаточно банальном в своей эффективности подходе к разработке, которым пренебрегают многие разработчики. Речь о достоинствах обертывания сторонних библиотек в кастомный API. С одной стороны, кажется, что это принесет только излишний бойлерплейт, но на самом деле, создавая врапперы, можно существенно упростить свою жизнь в будущем:

  • Многие библиотеки при обновлении ломают API, из-за чего приходится делать рефакторинг всех компонентов, использующих библиотеку. Если сделать обертку для библиотеки на ранних этапах разработки, менять придется только ее.
  • Технологии развиваются, вместе с ними меняются инструменты. Вполне возможно, в будущем придется отказаться от использования текущих библиотек и мигрировать на другие. Если есть враппер — менять придется только его.
  • В обертку можно вынести все дополнительные настройки, свойственные для проекта в целом, чтобы не повторять их при каждом вызове функций библиотеки.
  • Обертку можно использовать для расширения функциональности библиотеки.
  • Не все библиотеки спроектированы с учетом возможностей для тестирования. Обертка решит эту проблему.
  • Одну и ту же обертку можно использовать в нескольких проектах одновременно. Внося изменения в один проект, мы почти автоматически получаем их в другом.
 

Инструменты и библиотеки

  • dexcalibur — основанный на Frida инструмент анализа и перехвата функций приложений;
  • Holdy — контейнер для работы с множеством фрагментов;
  • jpegkit-android — обертка для высокоэффективной библиотеки libjpeg-turbo;
  • AsynKio — простая в использовании библиотека для выполнения сетевых запросов;
  • ColorPickerView — библиотека для выбора цвета на изображении с помощью цветового круга;
  • retroauth — обертка вокруг Retrofit для выполнения аутентификационных реквестов;
  • injectvm-binderjack — пример инъекции кода Java/Kotlin в любой JVM-процесс Android, включая system_server;
  • InlineActivityResult — библиотека, позволяющая запустить startActivityForResult и получить результат в колбэке вместо ожидания интента;
  • fuzzywuzzy-koltin — Kotlin-форк библиотеки FuzzyWuzzy для сравнения строк с помощью алгоритма Левенштейна;
  • bento — библиотека для создания модульного UI;
  • kyrie — аналог VectorDrawable и AnimatedVectorDrawable с возможностью паузы, создания на лету и другими полезными функциями;
  • Stepper — библиотека для создания пошаговых визардов первого запуска;
  • AndroidMalware_2019 — сборник семплов найденной в 2019 году малвари.

Check Also

В королевстве PWN. Препарируем классику переполнения буфера в современных условиях

Сколько раз и в каких только контекстах не писали об уязвимости переполнения буфера! Однак…

Оставить мнение