Android: Биометрия в Android 11 и новый тип вымогателя
Life-Hack [Жизнь-Взлом]/ХакингСегодня в выпуске: улучшение системы биометрической аутентификации в Android 11, ransomware, умеющий блокировать устройство без специальных разрешений, мифы о производительности Android, адаптация приложения к современным требованиям приватности, быстрая мультиплатформенная NoSQL база данных, бенчмарк библиотек загрузки изображений, запуск кода Java на устройстве без создания APK. А также: подборка инструментов пентестера и библиотек для разработчиков.
ПОЧИТАТЬ
Безопасность экрана блокировки в Android 11
Lockscreen and authentication improvements in Android 11 — статья разработчиков из команды безопасности Android об изменении в работе механизмов аутентификации по отпечаткам пальцев и снимку лица.
До Android 11 система аутентификации Android работала по следующим правилам:
- Пароль или PIN-код — считается наиболее надежным методом аутентификации и поэтому дает полный контроль над устройством без всяких ограничений.
- Отпечаток пальца или снимок лица — менее надежный, система запрашивает пароль после каждой перезагрузки телефона, а также через каждые 72 часа.
- Smart Lock — наименее надежный метод, поэтому на него накладываются те же ограничения, что и на биометрический метод, плюс он не позволяет получить доступ к аутентификационным ключам Keymaster (например, тем, что используются для платежей), а пароль запрашивает не через 72 часа, а уже через четыре.
В Android 11 появилось понятие надежности способа биометрической аутентификации. Теперь система учитывает, насколько надежный датчик отпечатков пальцев или сканер лица установлен в устройство, и может изменить поведение. Например, ненадежный способ аутентификации нельзя будет использовать для аутентификации в сторонних приложениях и для разблокировки доступа к KeyStore. Также для такого способа аутентификации тайм‑аут перед следующим запросом пароля будет снижен с 72 до 24 часов.
Всего есть три класса надежности датчиков (способов) биометрической аутентификации:
- класс 3 — надежный, запрос пароля через 72 часа, доступ к KeyStore и возможность использования в сторонних приложениях;
- класс 2 — слабый, запрос пароля через 24 часа, доступ к KeyStore, невозможно использовать в сторонних приложениях;
- класс 1 — удобный, запрос пароля через 24 часа, нет доступа к KeyStore, невозможно использовать в сторонних приложениях.
Их надежность определяется на основе процента ложных срабатываний, безопасности способа обработки биометрических данных и некоторых других параметров.

Ransomware нового типа
Sophisticated new Android malware marks the latest evolution of mobile ransomware — статья исследователей из Microsoft о новом типе ransomware, найденном на просторах интернета.
Малварь называется AndroidOS/MalLocker.B и в целом уже известна и достаточно хорошо изучена. Интерес исследователей вызвала новая разновидность этого вымогателя: она научилась блокировать устройство, показывая сообщение о выкупе без использования экранных оверлеев (SYSTEM_ALERT_WINDOW), возможности которых Google серьезно ограничила в последних версиях Android.
Вместо оверлея зловред использует так называемое полноэкранное уведомление, с помощью которого легитимный софт показывает экран звонка. Кроме текста (и других стандартных атрибутов), такое уведомление также содержит ссылку на активность (экран приложения), который и будет показан, когда уведомление появится в системе.

Однако один раз показывать сообщение о выкупе было бы бесполезно, так как пользователь смог бы нажать кнопку «Домой» или «Назад» и просто закрыть его. Поэтому зловред использует еще один прием: перезапускает активность в методе onUserLeaveHint().

Метод onUserLeaveHint() — это колбэк, который система вызывает перед тем, как активность исчезнет с экрана. Поэтому перезапуск активности в этом методе приводит к тому, что пользователь просто не может покинуть экран с сообщением о выкупе.
Сам экран с сообщением о выкупе при этом представляет собой обычный WebView с информацией от якобы полиции о найденных на устройстве компрометирующих пользователя материалах. В коде также была найдена отключенная модель системы машинного обучения, которая, скорее всего, должна была использоваться для подгонки сообщения под разрешение экрана устройства.

Еще одна интересная особенность зловреда — способ шифрования кода, а точнее, способ скрытия методов шифрования кода. В основном DEX-файле шифровальщика есть класс, содержащий методы для шифрования и расшифровки остальных частей приложения. На вход эти методы получают строку (которая на первый взгляд кажется ключом шифрования), а на выходе почему‑то возвращают объект класса Intent (такие объекты в Android используются для отправки сообщений другим приложениям).
На самом деле входная строка — это просто мусор, а сам интент содержит поле action, в котором как раз и хранится адрес расшифрованных данных.
РАЗРАБОТЧИКУ
Мифы о производительности Android
Busting Android performance myths — разбор старых и новых мифов о производительности Android.
- Приложения на Kotlin медленнее и больше приложений на Java. Конвертация приложения Google Drive на Kotlin показала, что размер и скорость запуска приложения практически не изменились, зато размер кодовой базы сократился примерно на 25%.
- Геттеры следует изменять на публичные поля. Некоторые разработчики отказываются от геттеров в пользу публичных полей (
foo.barпротивfoo.getBar()), чтобы повысить производительность. Однако это ничего не дает, потому что компилятор ART умеет инлайнить геттеры, превращая их в обычные обращения к полям. - Лямбды лучше заменять вложенными классами. Лямбды считаются очень медленными, и ты можешь встретить советы заменить их. На самом деле после компиляции лямбды превращаются в те же анонимные вложенные классы.
- Аллокация объектов и сборка мусора в Android — медленные. Когда‑то это действительно было правдой, но за последние годы разработчики сделали аллокатор объектов в десятки раз, а сборщик мусора — в несколько раз быстрее. Например, на Android 10 автоматическая аллокация объектов в тестах производительности ничем не отличается от аллокации и освобождения пула объектов вручную (именно такой способ рекомендовали использовать для сохранения производительности раньше).
- Профайлинг дебаг‑сборок — это нормально. Обычно при профайлинге приложения разработчики не обращают внимания, что имеют дело с debug-сборкой. В результате они анализируют производительность неоптимизированного кода и получают неверные результаты профайлинга.
- У MultiDex-приложений более медленный холодный старт. При превышении лимита на количество методов компилятор разбивает приложение на несколько исполняемых файлов DEX. Считается, что это приводит к снижению времени старта приложения. На деле если такой эффект и есть, то он проявляется только при наличии сотен файлов DEX. С другой стороны, MultiDex-приложения занимают на несколько процентов больше оперативной и постоянной памяти.
- Приложения содержат много мертвого кода. Это действительно так. Исследование показало, что приложение Google содержит большое количество никогда не используемого кода. Однако это не просто мертвый код, это код обработки ошибок, код поддержки старых версий Android и код редко используемых функций. Настоящий мертвый код, который не используется ни при каких обстоятельствах, удаляется оптимизатором R8.

Адаптация приложения к современным требованиям приватности
Adapt your app for the latest privacy best practices — статья о том, как написать правильное с точки зрения приватности пользователя приложение.
- Разрешение на «общение» с другими приложениями. Для приложений, собранных для Android 11 (targetSdkVersion 30), действуют ограничения на просмотр информации и коммуникацию с другими приложениями. Теперь приложение должно прямо указывать в манифесте, с какими приложениями оно может взаимодействовать и какие интенты может использовать. Официальная документация рассматривает множество примеров, покрывающих почти все возможные случаи. Плюс ко всему, если твое приложение использует content provider, чтобы расшаривать информацию другим приложениям, ты должен указывать флаг
Intent.FLAG_GRANT_READ_URI_PERMISSIONв любом интенте, расшаривающем данные провайдера: val shareIntent = Intent(Intent.ACTION_VIEW).apply {flags = Intent.FLAG_GRANT_READ_URI_PERMISSIONdata = // URI, расшариваемый с другим приложением}- Постепенный запрос разрешений. Исследования показывают, что пользователи склонны предоставлять приложениям полномочия, когда им требуется конкретная функциональность приложения, которая не будет работать без этого разрешения. Только 32% пользователей предоставляют разрешение потому, что доверяют разработчику приложения. Второй момент, который необходимо помнить: если приложение собрано для Android 11, оно больше не может запрашивать разрешение на определение местоположения в фоне (background location) сразу, сначала оно должно запросить разрешение foreground location.
- Корректный доступ к камере и микрофону. Android разрешает использовать доступ к камере и микрофону, только если приложение видимо на экране. Это же относится и к сервисам: foreground service имеет иконку в панели состояния, и поэтому он видим и может использовать камеру и микрофон. Android 11 вводит еще одно требование — сервис должен прямо указать, какие типы сенсоров он будет использовать:
android:foregroundServiceType = "microphone | location | camera"- Идентификаторы устройства. Android 10 запрещает чтение многих хардварных идентификаторов устройства, включая серийный номер SIM-карты и IMEI. Android 11 также закрывает возможность читать ICC ID. В качестве альтернативы можно использовать метод
getSubscriptionId(), который возвращает идентификатор, уникальный для каждой SIM-карты.
Быстрая мультиплатформенная NoSQL база данных
Announcing a painless Kotlin/Multiplatform NoSQL embedded database — анонс новой NoSQL базы данных для мультиплатформенных проектов на Kotlin.
Kodein-DB специально создана для несерверных проектов, в которых важно удобство использования и гибкость, а не возможность создавать сложные схемы хранения данных. Это крайне быстрая база данных, в некоторых операциях в десятки раз опережающая SQLite по скорости. Она очень проста в использовании и не требует никаких подготовительных шагов. Базу данных можно открыть и сразу использовать для сохранения объектов:
// Все модели должны быть сериализуемы и реализовать интерфейс Metadata
@Serializable
data class User(
// Каждый экземпляр должен использовать уникальный ID
override val id: String,
val firstName: String,
val lastName: String
) : Metadata
fun store(db: DB, user: User) { db.put(user) }
fun load(db: DB, id: String): User = db.get(db.newKey<User>(id)).model
fun test(db: DB) {
val id = UUID.randomUUID()
val user = User(id, "John", "Doe")
store(db, user)
val otherUser = load(db, id)
assertEquals(user, otherUser)
}
Kodein-DB кеширует все данные в оперативной памяти и умеет сообщать о выполненных операциях, поэтому ее можно использовать как единый источник истины (single source of truth), не дублируя состояние БД в оперативной памяти самостоятельно:
fun listenForChange(db: DB) {
db.on<ChatMessage>().register {
didPut {
addMessageToUI(it)
}
didDelete {
reloadList(
db.find<Chat>()
.byIndex("time")
.useModels(reverse = true) { it.toList() }
)
}
}
}
В настоящий момент проект находится в состоянии беты, но уже вполне пригоден для повседневного использования.

Бенчмарк библиотек загрузки изображений
Benchmarking Image Loading Libraries on Android — бенчмарк библиотек для сетевой загрузки изображений: Coil, Glide, Fresco, Picasso 2/3.
Библиотеки загрузки изображений нужны почти в каждом мобильном приложении. Они входят в must have список любых подборок библиотек. Поэтому выбор библиотеки важен. Разработчик одной из популярных библиотек Coil сравнил несколько библиотек и пришел к выводу, что по соотношению производительность/размер безоговорочно выигрывает Picasso. Это крайне компактная и производительная библиотека, однако она не обладает рядом функций, которые, например, есть в Coil, и не оптимизирует потребление оперативной памяти. Поэтому окончательный выбор следует делать исходя из текущих потребностей приложения.

Запуск кода Java на устройстве без создания APK
HOWTO: Running Java code directly on Android (without creating an APK) — небольшая заметка о том, как запустить исполняемый код на Android без создания пакета APK.
Допустим, у нас есть такой код на Java (необязательно вдаваться в подробности его реализации):
package com.example;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
public class HelloWorld {
public static void main(String[] args) throws ParseException {
Option version = new Option("v", "Print version");
Option help = new Option("h", "Print help");
Options options = new Options();
options.addOption(help);
options.addOption(version);
for (Option opt : new DefaultParser().parse(options, args).getOptions()) {
if (opt.equals(version)) {
String os = System.getProperty("os.arch");
System.out.println("Hello World (" + os + ") v0.1");
}
if (opt.equals(help)) {
new HelpFormatter().printHelp("Hello World", options);
}
}
}
}
Мы хотим запустить его прямо на устройстве, не создавая файл APK. Для этого его необходимо скомпилировать:
$ javac -source 1.7 -target 1.7 -d bin -cp lib/commons-cli-1.3.1.jar src/com/example/HelloWorld.java
В данном случае bin — это каталог, в который компилятор поместит полученный байт‑код Java, lib/commons-cli-1.3.1.jar — библиотека — зависимость кода, а src/com/example/HelloWorld.java — путь к исходному файлу.
Теперь полученный байт‑код Java необходимо преобразовать в байт‑код DEX, пригодный для исполнения на Android-смартфоне:
$ ./android-sdk-linux/build-tools/23.0.2/dx --output=helloworld.jar --dex ./bin lib/commons-cli-1.3.1.jar
В твоем случае путь к команде dx может быть другим (обычно каталог android-sdk-linux располагается в каталоге Android Studio или рядом с ним).
Далее необходимо создать shell-скрипт, который будет запускать наш код:
base=/data/local/tmp/helloworld
export CLASSPATH=$base/helloworld.jar
export ANDROID_DATA=$base
mkdir -p $base/dalvik-cache
exec app_process $base com.example.HelloWorld "$@"
Здесь app_process — это команда Android, которая запускает новую виртуальную машину. Точно таким же образом запускаются стандартные приложения для Android.
Теперь скрипт вместе с байт‑кодом можно перекинуть на устройство:
$ adb shell mkdir -p /data/local/tmp/helloworld
$ adb push helloworld.jar /data/local/tmp/helloworld
$ adb push helloworld.sh /data/local/tmp/helloworld
$ adb shell chmod 777 /data/local/tmp/helloworld/helloworld.sh
И запустить:
$ adb shell /data/local/tmp/helloworld/helloworld.sh -v
ИНСТРУМЕНТЫ
- MEDUZA — скрипт для быстрого отключения SSL-валидации, включая SSL Pinning в приложениях для Android;
- Cryptoguard — утилита для обнаружения неправильного использования криптографии в приложении;
- Crylogger — еще одна утилита для поиска ошибок в использовании криптографии.