Сегодня в выпуске: новый security-чип в смартфоне Pixel 3, защита от атак с использованием ROP в ядре Linux, уроки фаззинга, смартфон, ворующий рекламные деньги, прошлое, настоящее и будущее пакетов APK. А также несколько статей для программистов: управление приоритетами потоков, разбор системы типов Kotlin и рассказ о Contracts — новой функции Kotlin 1.3.
Building a Titan: Better security through a tiny chip — рассказ о security-чипе Titan M, который используется в смартфонах Pixel 3 и Pixel 3 XL. Чип разработан и производится самой Google, а в число его функций входят:
По сути, Titan M — это аналог Secure Enclave, который Apple уже несколько лет предустанавливает в свои смартфоны. Благодаря тому что Titan M — это выделенный микрокомпьютер (на базе ARM Cortex-M3), не связанный с основным процессором, он гораздо более устойчив к любым атакам, включая «неисправляемые» Rowhammer, Spectre и Meltdown.
Google обещает открыть код прошивки Titan M в скором времени.
Control Flow Integrity in the Android kernel — статья разработчиков Android о применении технологии Control Flow Integrity для защиты ядра Linux от атак с использованием метода ROP (Return Oriented Programming).
Современные эксплоиты в своей работе часто полагаются на модификацию указателей на функцию и адресов возврата. Это позволяет обойти ограничение на исполнение стека и сегмента данных с помощью переиспользования кусков самого приложения.
Технология Control Flow Integrity (CFI) предназначена для борьбы с такими эксплоитами. При ее включении компилятор строит граф вызовов функций и встраивает код сверки с этим графом перед каждым вызовом функции. Если вызов происходит по отклоняющемуся от графа адресу, приложение завершается.
Ранее разработчики Android уже включили CFI для нескольких системных компонентов в Android 8. В Android P покрытие расширилось и теперь включает в себя медиафреймворки, а также стек NFC и Bluetooth. Теперь поддержка реализована для ядра версий 4.9 и 4.14.
Writing the worlds worst Android fuzzer, and then improving it — занимательная статья о том, как обычно проводят фаззинг и какую информацию взломщик может получить с его помощью.
Началось все с того, что автор решил устроить фаззинг файловой системы Android и попробовать прочитать и записать во все встреченные файлы случайный набор байтов. Расчет здесь на то, что в Android (а точнее, Linux) несколько виртуальных файловых систем (/dev
, /proc
, /sys
) хранят не реальные, а синтетические файлы, с помощью которых можно изменять настройки ядра и работать с железом.
Как оказалось, долго фаззер работать не мог и в определенный момент просто блокировался при попытке чтения файла. Чтобы решить эту проблему, автор распараллелил фаззер на 128 потоков, и… смартфон ушел в kernel panic. То же повторилось с каждым из протестированных смартфонов.
Анализ лога из /proc/last_kmsg
показал, что произошло что-то вроде heap corruption, и, если поковырять дальше, можно написать эксплоит для получения прав root в системе. А DoS-эксплоит фактически уже существует.
Но что, если файла /proc/last_kmsg
нет? Например, в Galaxy S5 такой файл отсутствует, а значит, мы не сможем узнать, обращение к какому файлу привело к панике ядра. В этом случае для начала сокращаем область работы фаззера, пробуем, например, только каталог /sys
. Это срабатывает, проблема в одном из его файлов. Проходимся по подкаталогам и выясняем, что ядро падает при записи в один из файлов /sys/kernel
, а если точнее — /sys/kernel/debug/
.
Чтобы не перебирать все файлы этого подкаталога вручную, пишем сервер, который будет работать на компе и принимать имена файлов от фаззера по мере его работы. Какое последнее имя получим перед падением, то нам и нужно.
Последним файлом оказался /sys/kernel/debug/smp2p_test/ut_remote_gpio_inout
. Теперь написать DoS-эксплоит проще простого.
Автор отмечает, что в современном мире остается все меньше телефонов, которые можно отправить в панику таким способом. Все дело в правилах SELinux, которые закрывают доступ к большинству системных файлов. Однако некоторые устройства до сих пор уязвимы.
Avast Threat Labs finds Android device firmware that reroutes ad network revenue to unknown accounts — интересная история в блоге Avast о прошивке дешевого китайского телефона, которая не только содержит привычную всем рекламу на экране блокировки и умеет скрыто устанавливать приложения, но и перенаправляет выручку от рекламы в сторонних приложениях на другие аккаунты.
Прошивка включает в себя несколько хуков, которые срабатывают, когда то или иное приложение обращается к настройкам (SharedPreferences), файлам внутри своего пакета (AssetManager и ZipEntry) или передает данные между своими компонентами в бандлах (Bundle). Код хука ищет в полученных данных токен рекламной сети и подменяет его своим, так что все деньги от показа рекламы получает не разработчик приложения, а третье лицо.
Специалисты из Avast не сообщают модель устройства, на которое была предустановлена эта прошивка. Известно лишь, что смартфон работал на Android 6 и продавался в Европе. Хотя, судя по файлу с информацией о приложениях, токены которых перехватывала прошивка, основной целью были китайские пользователи.
Typical Kotlin — статья о системе типов языка Kotlin и ее особенностях. Несколько интересных моментов:
Double
— это подтип типа Double?
, а тот, в свою очередь, подтип типа Number?
, который подтип типа Any?
. Таким образом, значение типа Double
можно сохранить в переменной типа Number?
.Hello
, ты также объявишь тип Hello?
.public object Unit { override fun toString() = "kotlin.Unit" }
Nothing
(и его близнец Nothing?
). Функция, объявленная с возвращаемым типом Nothing
, никогда не закончит исполнение, так как значение, которое нужно вернуть, не существует. Nothing
удобно использовать, чтобы подсказать компилятору и среде разработки, что функция никогда не закончит свое исполнение (или закончит выбросом исключения). В этом случае среда разработки будет подсвечивать такие функции, подсказывая разработчику, что ее исполнение не закончится.Nothing
— это так называемый bottom type, то есть подтип любых других типов. А return и throw — это полноценные выражения, которые возвращают тип Nothing
. Комбинируем эти два утверждения вместе и получаем два вполне рабочих примера кода:fun calculate(someParam: Int?) { val x = someParam ?: throw IllegalArgumentException("someParam must not be null") val y = x * 2 println(y) }
fun calculate(someParam: Int?) { someParam ?: return val y = someParam * 2 println(y) }
Exploring Android Thread Priority — статья о том, как Android распределяет приоритеты между потоками и как эти приоритеты изменить. Суть в следующем. В Android (а точнее, в Linux) у каждого потока исполнения есть собственный приоритет, который варьируется в пределах от –20 до 19, где меньшее число означает более высокий приоритет.
Плюс ко всему ядро Linux объединяет потоки в так называемые контрольные группы (cgroups) в зависимости от разных условий, таких, например, как видимость приложения на экране. Размещение потоков того или иного приложения в определенной группе автоматически накладывает на них ограничения. Так, потоки, помещенные в группу Background (то есть относящиеся к приложениям в фоне), получают всего 5% от общего процессорного времени.
По умолчанию любые потоки одного приложения получают равный приоритет с основным потоком, а значит, могут влиять на его исполнение. Чтобы этого избежать, следует снизить приоритет фоновых потоков с помощью одного из двух методов:
Thread.setPriority()
— принимает значения от 1 до 10, где 10 — самый высокий приоритет;Process.setThreadPriority()
— принимает стандартные для Android/Linux значения от –20 до 19.Соотношение этих двух шкал приоритетов такое:
Пример изменения приоритета потока:
public class ThreadPriority { Thread thread = new Thread(new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Твой код здесь } }, "worker-thread"); thread.start(); } }
В оригинальной статье также приведены примеры изменения приоритета HandlerThread, Intent Service и других.
Discovering Kotlin Contracts — статья о новинке Kotlin 1.3 под названием «контракты» (contracts). Они решают проблему, с которой сталкивался любой разработчик. Простой пример:
@Test fun testMyTokenLength() { val token : String? = getMyToken(); assertNotNull(token) assertEquals(42, token.length) }
Ты не сможешь его скомпилировать, потому что компилятор скажет следующее:
Error(5, 22): Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
Так происходит потому, что, по мнению компилятора, token может иметь значение null, а значит, мы должны проверить его на null перед тем, как вызывать метод length. Ну либо обойти предупреждение компилятора, написав так: token!!.lenght
. А это не очень красиво.
В то же время мы, в отличие от компилятора, знаем, что token к моменту вызова метода length уже не может быть null, потому что проверка на null была в прошлой строке. Контракты нужны как раз для того, чтобы передать наше знание компилятору.
Контракты базируются на идее эффектов (effects), которые представляют собой своего рода кусочки знаний, относящиеся к той или иной функции. На данный момент существует четыре эффекта:
Returns(value)
— функция успешно завершается и/или возвращает значение такого-то типа;ReturnsNotNull
— функция возвращает не null значение;ConditionalEffect(effect, booleanExpression)
— если эффект сработал, то следующее выражение верно;CallsInPlace(lambda, kind)
— ограничение на место и количество вызовов лямбды.Не стоит пытаться это понять, просто взгляни на следующий код:
fun assertNotNull(actual: Any?) { contract { returns() implies (actual != null) } org.junit.Assert.assertNotNull(actual) }
Это обертка для стандартной функции assertNotNull из пакета JUnit. Мы добавили к ней контракт, который сообщает компилятору и среде разработки, что если функция успешно завершается, значит, переданное ей в аргументе значение не равно null. Если подставить эту обертку вместо оригинала в приведенный в начале пример, компилятор ругаться не будет.
Android APK signature scheme v3: context and new opportunities — краткая история цифровых подписей приложений для Android и объяснение нового формата цифровой подписи в Android 9.
Изначально пакет с приложением для Android (APK) представлял собой почти точную копию пакета JAR, который, в свою очередь, был просто архивом ZIP с несколькими дополнительными файлами в каталоге META-INF
. Эти файлы содержали список всех файлов пакета, их криптографические хеши, а также открытый криптографический ключ, с помощью которого были подписаны списки хешей.
Все это позволяло убедиться в целостности содержащихся в пакете файлов, а также подтвердить, что пакет не был изменен после создания автором. Другими словами, устанавливая обновление приложения для Android, можно быть уверенным — оно создано тем же человеком, что и предыдущая версия.
Но есть проблема. Атака Janus показала, что существует возможность внедрить код в APK, не изменяя его цифровую подпись. Для этого можно дописать DEX-файл в начало секций ZIP-файла, и Android не учтет их при верификации файла, но при этом файл можно будет запустить как исполняемый: это будет одновременно и пакет с приложением, и исполняемый файл.
Для решения этой и схожих проблем Google предложила формат цифровой подписи APK signature scheme v2. Его суть в том, чтобы добавить к APK-файлу дополнительный блок, который будет содержать цифровую подпись всего APK целиком, а не отдельных его частей.
Поддержка scheme v2 появилась в Android 7, а уже в Android 9 добавилась поддержка APK signature scheme v3. Новый формат цифровой подписи похож на предыдущий, но обладает одной отличительной чертой: он поддерживает ротацию криптографических ключей. Это позволяет разработчикам без проблем менять цифровую подпись для своих приложений, не заставляя пользователей удалять старую версию приложения перед установкой новой.
Android Top Animation Libraries — краткий обзор пяти библиотек, позволяющих реализовать анимацию в приложении:
Читайте также
Последние новости