Сегодня в выпуске: privacy-новшества Android Q Beta 1, извлечение SSL-сертификатов подопытного приложения из KeyStore, Kotlin и его параметры вещественного типа, польза значения null, вред языковых конструкций Kotlin и более удобный оператор if-else. А также: метод ускорения Android Studio, дампер, позволяющий снять память любого приложения, и свежая подборка библиотек.
 

Почитать

 

Android Q: privacy

Privacy in Android Q — документация Google об изменениях в механизмах доступа к информации в недавно выпущенном Android Q Beta 1. Основные нововведения:

  1. Запрет фонового доступа к местоположению. Раньше, если пользователь разрешал приложению использовать местоположение, оно могло сделать это в любой момент, даже если находилось в фоне. В новой версии у пользователя есть возможность выбрать, в каких ситуациях отдавать приложению свои координаты: в любое время или только пока приложение находится на экране.
  2. Запрет фонового доступа к буферу обмена. В Android нет отдельного разрешения на доступ к буферу обмена, любое приложение может прочитать или записать его содержимое (так получилось из-за технических особенностей этого механизма). Теперь доступ к буферу обмена могут получить только приложения, которые в данный момент находятся на экране либо являются клавиатурами и другими системами ввода. Нововведение убивает целый класс приложений — менеджеры буфера обмена.
  3. Запрет фонового запуска активностей. Текущие версии Android позволяют приложениям запускать другие приложения (а точнее, их активности) в любое время, независимо от того, находятся они на экране или нет. Это может запутать пользователя и открывает возможности для фишинга и других зловредных действий. Android Q запрещает фоновым приложениям запускать активности. В качестве альтернативы предлагается использовать уведомления, которые, в свою очередь, запустят нужную активность с помощью PendingIntent.
  4. Запрет на прямой доступ к карте памяти. С Android Q приложения больше не смогут получить прямой доступ к карте памяти (внутренней или внешней) с помощью разрешений READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE. Вместо этого следует использовать либо личный каталог приложения внутри /sdcard/Android (он создается автоматически и не требует разрешений), либо одно из разрешений, допускающих доступ к каталогам с фотографиями, видео и загрузками.
  5. Возможность отзыва разрешений у старых приложений. Система подтверждения разрешений приложений пользователем появилась еще в Android 6.0. Но работала она только в отношении софта, собранного для Android 6.0 и выше. Весь старый софт продолжал получать все нужные ему разрешения автоматически. В Android Q ситуация не изменилась, но теперь при запуске старого приложения пользователь будет видеть экран, с помощью которого сможет отозвать уже выданные системой разрешения.
  6. Запрет на включение/выключение Wi-Fi. В Android Q приложения больше не смогут включать и выключать Wi-Fi, вместо этого они должны использовать новую функцию settings-panels, которая показывает всплывающий диалог с переключателем выбранной настройки.
  7. Ограничение на доступ к IMEI и серийному номеру устройства. Чтобы прочитать эту информацию, теперь требуется разрешение READ_PRIVILEGED_PHONE_STATE.
  8. Запрет на доступ к информации о частоте использования контактов. Приложения, получающие доступ к базе контактов телефона, больше не смогут также получить информацию о том, как часто пользователь контактировал с теми или иными людьми.
  9. Рандомизация MAC-адреса. При скане сетей Android Q будет использовать рандомизированный MAC-адрес вместо настоящего. Это изменение должно защитить от отслеживания пользователя: некоторые магазины используют MAC-адрес для отслеживания посетителей, а торговые центры — для трекинга их перемещения.
  10. Другие изменения: запрет на доступ к файловой системе /proc/net, запрет на чтение серийных номеров подключенных USB-устройств до получения соответствующего разрешения, запрет на чтение параметров камеры без получения разрешения на использование камеры, необходимость иметь разрешение ACCESS_FINE_LOCATION, чтобы получить доступ ко многим функциям телефонии, Wi-Fi и Bluetooth, запрет на скрытое получение содержимого экрана устройства обходными путями.
Окно отзыва разрешений у старого софта
Окно отзыва разрешений у старого софта
 

Извлекаем SSL-сертификат приложения из KeyStore

Extracting Android KeyStores from apps — статья об извлечении SSL-сертификатов из приложения с помощью Frida.

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

KeyStore имеет методы load(KeyStore.LoadStoreParameter param) и load(InputStream stream, char[] password) для извлечения данных из хранилища. Мы можем переписать код этих функций с помощью Frida и сохранить данные на своей машине.

Код скрипта для Frida выглядит так:

setTimeout(function() {
    Java.perform(function () {
        keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');

        /* Переписываем функцию Keystore.load */
        keyStoreLoadStream.implementation = function(stream, charArray) {
            /* Если первый параметр null — запускаем оригинал */
            if (stream == null) {
                this.load(stream, charArray);
                return;
            }

            /* Отправляем сообщение, что функция найдена */
            send({event: '+found'});

            /* Читаем InputStream в буфер */
            var hexString = readStreamToHex (stream);

            /* Отправляем тип KeyStore */
            send({event: '+type', certType: this.getType()});

            /* Отправляем пароль */
            send({event: '+pass', password: charArray});

            /* Отправляем сертификат в текстовой форме */
            send({event: '+write', cert: hexString});

            /* Запускаем оригинальную функцию */
            this.load(stream, charArray);
        }
    });
},0);

/* Функция для чтения InputStream и его конвертации в ASCII */
function readStreamToHex (stream) {
    var data = [];
    var byteRead = stream.read();
    while (byteRead != -1)
    {
        data.push( ('0' + (byteRead & 0xFF).toString(16)).slice(-2) );
        /* <------------ binary to hex -----------> */
        byteRead = stream.read();
    }
    stream.close();
    return data.join('');
}

Кроме этого скрипта, также понадобится скрипт, работающий на компе (именно ему приведенный выше скрипт отправляет данные с помощью функции send). Как работать с Frida и запустить скрипт, мы уже рассказывали в статье «Инъекция для андроида».

Результат работы скрипта
Результат работы скрипта
 

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

 

Более удобный if-else для Kotlin

Kotlin: when if-else is too mainstream — краткая заметка о том, как создать более удобный аналог оператора if-else, который можно использовать так:

val condition = true
val output = condition then { 1 + 1 } elze { 2 + 2 }

Реализация такого «оператора» умещается в десять строк:

infix fun <T>Boolean.then(action : () -> T): T? {
    return if (this)
        action.invoke()
    else null
}

infix fun <T>T?.elze(action: () -> T): T {
    return if (this == null)
        action.invoke()
    else this
}

Ключевое слово infix используется, чтобы функции-расширения then и elze можно было вызывать без необходимости ставить точку перед ними.

 

Kotlin и параметры вещественного типа

How Reified Type makes Kotlin so much better — краткая статья о том, как параметры вещественного типа могут облегчить твою жизнь. В официальной документации приводится только один пример использования такого типа параметров — для облегчения передачи классов внутрь функции. Например, вместо того чтобы писать так:

private fun <T : Activity> Activity.startActivity(
        context: Context, clazz: Class<T>) {
    startActivity(Intent(context, clazz))
}

startActivity(context, NewActivity::class.java)

можно написать так:

inline fun <reified T : Activity> Activity.startActivity(
        context: Context) {
    startActivity(Intent(context, T::class.java))
}

startActivity<NewActivity>(context)

Но на этом полезные качества вещественных типов не заканчиваются. Еще один пример — функция для получения данных из бандла:

fun <T> Bundle.getDataOrNull(): T? {
    return getSerializable(DATA_KEY) as? T
}

val bundle: Bundle? = Bundle()
bundle?.putSerializable(DATA_KEY, "Testing")
val strData: String? = bundle?.getDataOrNull()
val intData: Int? = bundle?.getDataOrNull() // Crash

Последняя строка приведет к падению приложения, так как не совпадают ожидаемый тип данных и тип, возвращаемый функцией. Исправить это можно так:

private inline fun <reified T> Bundle.getDataOrNull(): T? {
    return getSerializable(DATA_KEY) as? T
}

val bundle: Bundle? = Bundle()
bundle?.putSerializable(DATA_KEY, "Testing")
val strData: String? = bundle?.getDataOrNull()
val intData: Int? = bundle?.getDataOrNull() // Null

Также вещественные типы можно использовать для эмуляции перегрузки методов на основе возвращаемого значения (Kotlin и Java по умолчанию разрешают выполнять перегрузки только на основе аргументов):

inline fun <reified T> Resources.dpToPx(value: Int): T {
    val result = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        value.toFloat(), displayMetrics)

    return when (T::class) {
        Float::class -> result as T
        Int::class -> result.toInt() as T
        else -> throw IllegalStateException("Type not supported")
    }
}

val intValue: Int = resource.dpToPx(64)
val floatValue: Float = resource.dpToPx(64)
 

Kotlin и польза null

When You Should Use Null in Kotlin — небольшая заметка о пользе значения null.

В среде программистов на Kotlin использование nullable-типов данных считается дурным тоном. Но автор объясняет, что благодаря особенностям Kotlin (null safety) использование null становится скорее преимуществом, чем недостатком.

Значение null можно использовать для индикации отсутствия какого-либо значения или недоступности данных. И если в Java в этом случае ты легко мог совершить «ошибку на миллион долларов» (например, обратиться к методу null-объекта и уронить приложения), то Kotlin просто не позволит тебе этого сделать. Несколько примеров:

  1. Если объект != null, то выполняем код

    iuser?.let {
        handleNonNullUser(user)
    }
    
  2. Не выполняем функцию, если аргумент = null:

    fun handleUser(user : User?) {
        user ?: return
        // Твой код
    }
    
  3. Если объект = null, то

    val userName = user?.getName() ?: "Unknown"
    
 

Ускорение эмулятора Android

Android Emulator: Project Marble Improvements — статья разработчиков эмулятора Android об улучшениях в производительности, сделанных в версии 28.1 (эмулятор доступен в canary-канале). В целом все достаточно просто:

  1. Режим «на батарейке» по дефолту. Раньше эмулятор сообщал работающему в нем Android, что устройство питается от сети, а это приводило к излишней фоновой активности (в частности, система начинала обновлять приложения). Теперь эмулятор «работает» на батарейке.
  2. Возможность остановить эмулятор. Появились две ADB-команды, позволяющие полностью приостановить/возобновить работу эмулятора:

    $ adb emu avd pause
    $ adb emu avd resum
    

    При сборке и установке приложения эмулятор будет разбужен автоматически.

  3. Оптимизация механизма отрисовки. Новый механизм позволил на 8% улучшить производительность стрессового приложения.
  4. Оптимизация I/O-кода на macOS. Версия для macOS теперь использует системный вызов kqueue вместо select для мониторинга дисковых операций. Это позволило сократить оверхед с 10 до 3%.
  5. Headless-режим. Теперь эмулятор можно запустить без интерфейса и вывода картинки на экран. Эту функцию можно использовать для запуска автоматизированных UI-тестов.
График использования процессора. Слева: при работе от сети и фоновом обновлении приложений. Справа: при работе от аккумулятора
График использования процессора. Слева: при работе от сети и фоновом обновлении приложений. Справа: при работе от аккумулятора
 

Вред (некоторых) выражений Kotlin

Advocating against (some) Kotlin expressions — статья о вреде использования некоторых языковых конструкций Kotlin.

Когда ты программируешь на Kotlin, плагин Android Studio настоятельно предлагает конвертировать все подряд в выражение. Особенно раздражающим это бывает, например, когда плагин предлагает вынести return из условного оператора или оператора выбора. Простой пример:


Проблема этого кода в том, что он отрывает контекст от значения. Конкретно этот пример не раскрывает проблему целиком, но представь, что оператор выбора получился действительно длинным и не умещается целиком на экран. Ты видишь кусок кода и задаешься вопросом: а что это вообще?


Тебе приходится проматывать код вверх, чтобы просто узнать, что происходит со значением rawPizza дальше. А теперь представь, что ты читаешь diff на GitHub, который, кроме измененной строки, показывает только три строки кода сверху и снизу.

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


 

Ускоряем Android Studio

Is Your Android Studio Always Slow? Here’s How to Speed Up Immediately — краткая статья об ускорении Android Studio, интересная только наличием списка плагинов, которые можно безболезненно отключить в 90% случаев:

  • Android APK Support;
  • Android Games;
  • Android NDK;
  • App Links Assistant;
  • Copyright;
  • Coverage;
  • CVS Integration;
  • Editor Config;
  • Fabric for Android Studio;
  • Firebase (App Indexing, Services, Testing);
  • GitHub;
  • Google (Cloud Tools Core, Cloud Tools for Android, Developer Samples, Login, Services);
  • Markdown Support;
  • Mercurial integration;
  • hg4idea;
  • Settings repository;
  • Subversion integration;
  • Task management;
  • Test recorder;
  • TestNG-J;
  • YAML.
 

Инструменты

  • fridump3 — универсальный дампер памяти на основе Frida, совместим с iOS, Android и Windows;
  • Stringer — инструмент для генерации локализованной базы строк из CSV-файла.
 

Библиотеки

  • ExpandableBottomBar — кастомизируемая анимированная панель навигации для нижней части экрана приложения;
  • bubble-navigation — еще одна панель навигации;
  • android_dbinspector — библиотека для просмотра содержимого базы данных приложения прямо на устройстве;
  • Android-BackgroundChart — фоновое изображение с графиком;
  • ShowMoreText — кастомный TextView, позволяющий показывать больше текста по клику на ссылку (например, «Подробнее»);
  • headless-wifi-manager — библиотека для управления конфигурацией Wi-Fi других устройств через Nearby API;
  • crashx — очередная библиотека краш-репортинга, показывает окно с информацией об ошибке при падении приложения;
  • MultiProgressBar — множественный прогресс-бар в стиле Instagram Stories;
  • mr-clean — библиотека для очистки логов от важной персональной информации;
  • KBarcode — библиотека для реализации сканера штрих-кодов;
  • ProfileBar — эффектная реализация страницы профиля пользователя/автора/героя;
  • Kaskade — однонаправленный state-контейнер для хранения состояния объектов;
  • audio-visualizer-android — библиотека визуализации аудио;
  • glimpse-android — библиотека для обрезки изображений, учитывающая положение объектов.

1 комментарий

  1. Аватар

    LinO_dska

    30.03.2019 at 23:42

    Разве рандомизации мак адреса не было на андроиде? В ios 5 его ещё добавили. Думал, что в андроиде где-то тогда же он и появился. То есть хотите сказать, что любой автобус и поезд с московским вайфаем мог легко отслеживать всё это время перемещение всех андроид пользователей?

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

Check Also

Хроники битвы при Denuvo. Как «непробиваемая» игровая защита EA Origin оказалась пробиваемой

Защита от пиратства Denuvo пришла на смену SecuROM и связана с одним действующим лицом – Р…