Сегодня в выпуске: история о том, как Google безуспешно борется с малварью, статья о платной разблокировке загрузчика Huawei, гайд по борьбе с упаковщиками с помощью Frida. А также: подборка трюков и советов для Kotlin-программистов, инструменты профайлинга сетевых функций, лучшее объяснение отличия короутин от потоков и, конечно же, очередная подборка свежих библиотек и инструментов.
Combating Potentially Harmful Applications with Machine Learning at Google: Datasets and Models — рассказ разработчиков команды безопасности Android о том, как работает система Google Play Protect, которая выявляет вредоносные приложения в Google Play и на смартфонах пользователей. Несколько тезисов:
На фоне всей этой бравады стоит напомнить, что в тестах антивирусов Google Play Protect набирает 0 очков и плетется в конце рейтинга. По информации на январь 2018-го он смог обнаружить лишь 63% вирусов.
Bootloader unlocking is still possible for Huawei and Honor devices, but it’ll cost you — интересная заметка о страданиях владельцев смартфонов Huawei, желающих разблокировать загрузчик своего устройства.
В мае этого года Huawei официально заявила, что больше не будет предоставлять коды для разблокировки загрузчиков своих смартфонов (а также смартфонов своего суббренда Honor). Загрузчик отвечает за проверку целостности и цифровой подписи ядра ОС, а также за возможность прошивки устройства, так что те, кто не успел получить свой код разблокировки, теперь не могут установить на нее кастомную прошивку или даже получить права root с помощью Magisk.
Выяснилось, однако, что совсем недавно сервис FunkyHuawei, занимающийся выпуском утилит для восстановления окирпиченных смартфонов и смены их региона, начал предоставлять возможность разблокировки загрузчика всех моделей Huawei, включая самые свежие. Но есть один нюанс: стоимость кода разблокировки составляет 55 долларов, и он привязан к IMEI устройства, а значит, не может быть использован повторно.
В комментариях к статье уже появились шутки о том, что FunkyHuawei принадлежит самой Huawei и таким образом она пытается дополнительно заработать на пользователях. Шутки шутками, но вопрос о том, где сервис берет коды разблокировки, весьма серьезный. Либо это действительно Huawei, либо коды у нее просто крадут, либо алгоритм их генерации настолько слабый, что его удалось подобрать. Все три варианта не в пользу компании.
How-to Guide: Defeating an Android Packer with FRIDA — вводная статья об использовании Frida для помощи в анализе вируса.
Дано: вредоносное приложение с подозрительным файлом внутри пакета и небольшим сильно обфусцированным исполняемым dex-файлом. Анализ логов запуска logcat показывает, что приложение при работе создает и загружает еще один dex-файл (запакованный в JAR), а это значит, что, скорее всего, первый исполняемый файл — это всего лишь загрузчик (а точнее, упаковщик), а найденный ранее подозрительный файл — зашифрованный код приложения. При загрузке приложения упаковщик дешифрует файл и загружает его. Но есть одна проблема — сразу после загрузки дешифрованный файл удаляется и его невозможно проанализировать.
К сожалению, декомпиляция и статический анализ упаковщика ничего не дают — он слишком сильно обфусцирован и почти непригоден для чтения. Однако запуск приложения под управлением трассировочной утилиты strace показывает, что удаление происходит с помощью системного вызова unlink
.
Конечная идея — переопределить с помощью Frida код функции unlink
так, чтобы она ничего не удаляла. В этом случае исследователь сможет просто достать расшифрованный dex-файл из устройства и проанализировать его. Код функции перехвата для Frida:
console.log("[*] FRIDA started"); console.log("[*] skip native unlink function"); var unlinkPtr = Module.findExportByName(null, 'unlink'); Interceptor.replace(unlinkPtr, new NativeCallback(function (){ console.log("[*] unlink() encountered, skipping it."); }, 'int', []));
Kotlin fun and education on Twitter — создатель Kotlin Academy и автор книги «Android Development with Kotlin» Марсин Москала (Marcin Moskala) рассказывает об интересных трюках, которые можно провернуть в Kotlin.
В Kotlin fun
— это зарезервированное ключевое слово, но его можно использовать, если заключить в обратные кавычки или написать с большой буквы. Те же правила действуют в отношении любых других ключевых слов.
Используя обратные кавычки, можно пойти еще дальше и включить в имена не только ключевые слова, но и пробелы, и даже смайлы.
Как и многие другие языки, Kotlin поддерживает перегрузку операторов.
fold
— одна из самых мощных операций для работы с коллекциями. Она объединяет все элементы коллекции с помощью указанной функции. Например, с помощью fold
очень легко сложить или перемножить все элементы. Но можно сделать и более интересные вещи.
Еще несколько полезных функций для выполнения операций над несколькими последовательными элементами.
Various methods to debug HTTP traffic in Android applications — статья о том, как выполнять профайлинг сетевых функций приложения. Автор предлагает использовать пять различных методов.
Android Profiler — включен в состав Android Studio. Он показывает объем входящих и исходящих данных, задержки и даже позволяет взглянуть на сами данные (если приложение использует HttpURLConnection или OkHttp). По умолчанию сетевой профайлер отключен. Чтобы включить его, необходимо зайти в меню Run → Edit Configurations, открыть таб Profiling и поставить галочку напротив Enable advanced profiling. После этого он появится в окне стандартного профайлера (запускается через нижнюю панель Android Studio).
OkHttp Profiler plugin — плагин Android Studio для отладки реквестов OkHttp. Умеет показывать JSON в виде дерева и генерировать модели для парсера GSON. Недостаток: требуется установка плагина и модификация приложения.
Stetho — инструмент отладки приложений с помощью Chrome DevTools. Кроме инструментов для анализа лайотов и баз данных, включает в себя мощный сетевой профайлер. Требует установки библиотеки и модификации приложения.
Charles Proxy — десктопный прокси со встроенным сниффером и множеством дополнительных функций: это мониторинг сокетов, модификация сетевых пакетов, генератор реквестов и многое другое. Требует настройки на эмуляторе/телефоне прокси и установки SSL-сертификата (в случае если требуется отладка HTTPS-трафика). К тому же стоит 50 долларов (есть триальная версия).
AppSpector — инструменты профайлинга для Android и iOS. Позволяют просматривать логи, изучать базы данных и сетевые запросы. Необходима модификация приложения и регистрация на сайте. Управление только через веб-сайт разработчиков, так что возникает вопрос о конфиденциальности данных.
What you didn’t know about arrays in Kotlin — познавательная и полезная заметка о том, почему не стоит использовать массивы в data-классах в Kotlin.
Data-классы в Kotlin — очень полезный элемент языка, позволяющий быстро создавать классы, не обременяя себя написанием однотипного кода. Возьмем, к примеру, следующий код:
data class NumArray(val name: String, val values: IntArray)
Он объявляет data-класс NumArray с двумя полями. Это всего одна строка кода, но в результате ты получишь класс с уже реализованными геттерами, сеттерами и функциями equals(), hash(), toString(). Тебе не придется писать их самому, и это сильно облегчает жизнь.
Но есть одна проблема: если ты создашь два одинаковых объекта этого класса и попробуешь их сравнить, то получишь отрицательный ответ:
val n1 = NumArray("1", intArrayOf(1,2,3,4)) val n2 = NumArray("1", intArrayOf(1,2,3,4)) val result = n1==n2 println("result=$result")
Получается, что автоматически генерируемый метод equals() не работает? На самом деле это не так. Дело в том, что в JVM есть баг, который приводит к тому, что сравнение массивов и коллекций происходит по-разному. Коллекции сравниваются структурно, то есть поэлементно, а при сравнении массивов верный ответ будет только в том случае, если это действительно один и тот же массив, а не два с одинаковым набором элементов.
Поэтому вместо массивов лучше использовать списки:
data class NumList(val name: String, val values: List<Int>))
Concurrent Coroutines — Concurrency is not Parallelism — одна из лучших статей для тех, кто хочет разобраться, что такое короутины Kotlin. Вместо того чтобы рассказывать о стейт-машинах и математических алгоритмах, автор говорит о том, что надо просто осознать разницу между понятиями concurrency и parallelism.
Роб Пайк, один из разработчиков Unix, Plan 9 и языка Go, говорит об этих понятиях так: concurrency — это когда ты имеешь дело со множеством вещей одновременно, а parallelism — это когда ты делаешь множество вещей одновременно. Продемонстрируем это утверждение кодом:
fun main() = runBlocking<Unit> { val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") } suspend fun doSomethingUsefulOne(): Int { delay(1000L) return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) return 29 }
Здесь происходит запуск двух короутин, каждая из которых засыпает на одну секунду, а затем возвращает определенное число. Основная короутина при этом дожидается завершения обеих дочерних короутин и печатает на экран сумму двух возвращенных чисел.
Приложение заканчивает свою работу ровно за одну секунду, что абсолютно логично. Но есть один важный нюанс: оно работает в одном потоке. Когда одна из дочерних короутин блокируется (в данном случае с помощью delay, а в реальном приложении из-за ожидания данных из сети или с диска), основная короутина продолжает свою работу. Это и есть тот случай, когда приложение «имеет дело» с множеством вещей, а не делает их одновременно.
Но мы можем исправить код так, чтобы он действительно делал несколько вещей одновременно:
val time = measureTimeMillis { val one = async(Dispatchers.Default) { doSomethingUsefulOne() } val two = async(Dispatchers.Default) { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") }
Такой код запускает каждую короутину в отдельном потоке (Dispatchers.Main — основной поток приложения, Dispatchers.Default — один из фоновых), и да, теперь приложение делает несколько вещей одновременно.
В этом примере от такой замены нет никакой пользы, но в реальном приложении в фоновые потоки можно отправить тяжелые вычисления, а запускать код, ожидающий данные из сети или с диска (или просто часто спящий), эффективнее в основном потоке. В конце концов, nginx, один из самых производительных веб-серверов, однопоточный (точнее, он использует по одному потоку на ядро, но сути это не меняет).
Advanced Kotlin Coroutines tips and tricks — еще одна статья о короутинах, в этот раз с советами по использованию.
1. Проблемы с Java API. Возьмем следующий пример:
runBlocking(Dispatchers.IO) { withTimeout(1000) { val socket = ServerSocket(42) socket.accept() } }
Подразумевается, что этот код запустит короутину, которая будет ждать подключения ровно одну секунду и затем будет прервана.
Но этого не произойдет, потому что socket.accept()
заблокирует поток короутины, до тех пор пока кто-нибудь действительно не подключится.
Обойти эту проблему можно с помощью suspendCancellableCoroutine
. Создадим небольшую функцию-помощник:
public suspend inline fun <T : Closeable?, R> T.useCancellably( crossinline block: (T) -> R ): R = suspendCancellableCoroutine { cont -> cont.invokeOnCancellation { this?.close() } cont.resume(use(block)) }
И слегка изменим наш пример:
runBlocking(Dispatchers.IO) { withTimeout(1000) { val socket = ServerSocket(42) socket.useCancellably { it.accept() } } }
Теперь все работает как надо.
2. launch vs. async. Это два самых используемых короутин-билдера в Kotlin. И тот и другой порождают новую короутину, но работают немного по-разному:
How To Customize Logcat Appearance in Android Studio — короткая заметка о том, как раскрасить вывод logcat в Android Studio для более удобного чтения. Для этого достаточно перейти в настройки, набрать в поиске logcat, и ты увидишь список уровней логирования от Debug до Assert. Выбирай один из них, отключай галочку Inherit values from и в опциях Foregraund, Background и других выбирай нужные цвета.
Автор статьи предлагает следующую схему цветов:
#bb2b2f
;#1194d6
;#db332f
;#0c890d
;#a8a8a8
;#bb7000
.Также он создал цветовую схему на базе тем Default и Dracula.
Читайте также
Последние новости