Сегодня в выпуске: 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 — библиотека для обрезки изображений, учитывающая положение объектов.

Check Also

Изучаем ПЛК. Краткий гайд по поиску уязвимостей в промышленных контроллерах

Если ты думаешь, что контроллеры, которые ставят в зданиях и на заводах, защищены намного …

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

  1. Аватар

    LinO_dska

    30.03.2019 at 23:42

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

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