Сегодня в выпуске: privacy-новшества Android Q Beta 1, извлечение SSL-сертификатов подопытного приложения из KeyStore, Kotlin и его параметры вещественного типа, польза значения null, вред языковых конструкций Kotlin и более удобный оператор if-else. А также: метод ускорения Android Studio, дампер, позволяющий снять память любого приложения, и свежая подборка библиотек.
Privacy in Android Q — документация Google об изменениях в механизмах доступа к информации в недавно выпущенном Android Q Beta 1. Основные нововведения:
READ_EXTERNAL_STORAGE
и WRITE_EXTERNAL_STORAGE
. Вместо этого следует использовать либо личный каталог приложения внутри /sdcard/Android
(он создается автоматически и не требует разрешений), либо одно из разрешений, допускающих доступ к каталогам с фотографиями, видео и загрузками.READ_PRIVILEGED_PHONE_STATE
./proc/net
, запрет на чтение серийных номеров подключенных USB-устройств до получения соответствующего разрешения, запрет на чтение параметров камеры без получения разрешения на использование камеры, необходимость иметь разрешение ACCESS_FINE_LOCATION
, чтобы получить доступ ко многим функциям телефонии, Wi-Fi и Bluetooth, запрет на скрытое получение содержимого экрана устройства обходными путями.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 и запустить скрипт, мы уже рассказывали в статье «Инъекция для андроида».
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 можно было вызывать без необходимости ставить точку перед ними.
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)
When You Should Use Null in Kotlin — небольшая заметка о пользе значения null.
В среде программистов на Kotlin использование nullable-типов данных считается дурным тоном. Но автор объясняет, что благодаря особенностям Kotlin (null safety) использование null становится скорее преимуществом, чем недостатком.
Значение null можно использовать для индикации отсутствия какого-либо значения или недоступности данных. И если в Java в этом случае ты легко мог совершить «ошибку на миллион долларов» (например, обратиться к методу null-объекта и уронить приложения), то Kotlin просто не позволит тебе этого сделать. Несколько примеров:
iuser?.let { handleNonNullUser(user) }
fun handleUser(user : User?) { user ?: return // Твой код }
val userName = user?.getName() ?: "Unknown"
Android Emulator: Project Marble Improvements — статья разработчиков эмулятора Android об улучшениях в производительности, сделанных в версии 28.1 (доступен в canary-канале). В целом все достаточно просто:
Возможность остановить эмулятор. Появились две ADB-команды, позволяющие полностью приостановить/возобновить работу эмулятора:
$ adb emu avd pause $ adb emu avd resum
При сборке и установке приложения эмулятор будет разбужен автоматически.
Advocating against (some) Kotlin expressions — статья о вреде использования некоторых языковых конструкций Kotlin.
Когда ты программируешь на Kotlin, плагин Android Studio настоятельно предлагает конвертировать все подряд в выражение. Особенно раздражающим это бывает, например, когда плагин предлагает вынести return
из условного оператора или оператора выбора. Простой пример:
Проблема этого кода в том, что он отрывает контекст от значения. Конкретно этот пример не раскрывает проблему целиком, но представь, что оператор выбора получился действительно длинным и не умещается целиком на экран. Ты видишь кусок кода и задаешься вопросом: а что это вообще?
Тебе приходится проматывать код вверх, чтобы просто узнать, что происходит со значением rawPizza
дальше. А теперь представь, что ты читаешь diff на GitHub, который, кроме измененной строки, показывает только три строки кода сверху и снизу.
Вывод: использование подобных выражений имеет смысл только для однострочных ветвлений, которые просто возвращают значение.
Is Your Android Studio Always Slow? Here’s How to Speed Up Immediately — краткая статья об ускорении Android Studio, интересная только наличием списка плагинов, которые можно безболезненно отключить в 90% случаев:
Читайте также
Последние новости