Как стать автором
Обновить

Прячем код по-настоящему: тёмные уголки обфускации R8 и ProGuard

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров1.4K

Обфускация — важная часть защиты Android-приложений. Однако даже если вы используете R8 или ProGuard - это не гарантия высокой устойчивости к тому что вас код не взломают. Стандартная настройка обфускации довольно простая и нацелена на уменьшение размера сборки, а не повышение защиты от взлома. В этой статье разберу как происходит обфускация в стандартной конфигурации, как можно её сделать сложнее для разбора, а по итогу и уникальной для каждой сборки.

Больше полезных материалов по Android разработке вы найдете в Telegram канале Android Broadcast

Что такое обфускация

Схема работы обфускатора кода
Схема работы обфускатора кода

Обфускация — это процесс преобразования имён в коде. Зачастую заменяются читаемые имена на нечитаемые, чтобы усложнить разбор кода. Например, UserManager → a, getUser() → b(). На вход обфускатор принимает словари и конфигурацию, которая содержит описание какие части кода не стоит переименовывать и другие настройки.

Инструменты ProGuard и R8 включают в себя этап обфускации, но также выполняют минификацию (удаление неиспользуемых частей кода) и оптимизацию (изменение кода с целью улучшения его работы по скорости, потреблению памяти и пр.).

Обфускаторы в ProGuard и R8 используют словари - набор слов из которых формируется новое имя. По умолчанию используется словарь состоящих из букв английского алфавита:

# Стандартный словарь R8/ProGuard
a
b
c
...
z

Когда слова в словаре заканчиваются, то начинается комбинирование с начала: a, b, c, ..., z, aa, ab, ac,..., az, ba, bb, ..., zzzzzz, ...

Словарь из всех возможных букв английского алфавита выбран потому, что он самый простой и позволяет делать имена элементов кода короткими, что положительно влияет на финальный размер файлов.

При этом стандартный словарь имеет несколько минусов:

  • Малое разнообразие имён ведет к тому, что проще угадать назначение

  • При повторной сборке будет полное соответствие прошлой сборке

  • Автоматические инструменты реверс инжениринга кода легко строят шаблоны связи кода в разных сборках

Всё это ведет к ослаблению защиты вашего кода, но усилить её можно с помощью задания собственного словаря.

Как использовать собственный словарь

R8/ProGuard позволяют задать собственные словари для обфускации кода через файл конфигурации. Если не указать словарь, то будет использоваться стандартный

# Добавьте в файл конфигурации R8/ProGuard

# Задание словаря для обфускации переменных и методов
-obfuscationdictionary obfuscation-dictionary.txt

# Словать для имек классов
-classobfuscationdictionary obfuscation-dictionary.txt

# Словарь для пакетов
-packageobfuscationdictionary obfuscation-dictionary.txt

Продвинутые словари для обфускации

Словарь из ключевых слов Java

Один из интересных словарей который можно взять - ключевые слова Java. Их запрещено использовать в Java как имена методов/классов, но R8/ProGuard работают на уровне байткода и там такие имена полностью валидны. Будет полезен для имен переменны и методов

# Пример из словаря из ключевых слова Java
do
if
for
int
new
...
instanceof
synchronized

Пример восстановленного Java кода после обфускации с применением словаря ключевых слов

// Код будет с ошибками из-за использования ключевых слов в названиях
package class;

public class static {
  private String final;
  private String volatile;

  public static(String interface, String extends) {
    this.final = interface;
    this.volatile = extends;
  }

  public boolean if() {
    return "admin".equals(final) && "1234".equals(volatile);
  }
}

Словарь из запрещенных названий файлов

В Windows есть запрещенные названия файлов и при попытке назвать так файл будет ошибка. Хорошая идея чтобы использовать такой словарь для названия классов, чтобы при попытке реверс инжиниринга привести к ошибке

# Пример из словаря с запрещенными именами файлов в Windows
aux
Aux
aUx
...

Все словари сохранил файлами в Gist

Словари с длинными именами ключей могут приводить к увеличению размера APK и размера приложения на диске после установки

Теперь у вас есть собственный словарь, но все мы не избавились от одной проблемы - словарь один и тот же каждый раз. Давайте теперь сделаем генерацию словаря для каждой сборки уникальной

Уникальный код после обфускации. Рандомизация словаря

Хорошей практикой защиты является использование уникального словаря для каждой обфускации вашего кода. Самый простой способ - взять ваш собственный словарь и перед запуском обфускации случайным образом менять в нем слова. Если вы используете Gradle, то это будет удобно сделать с помощью добавления Gradle task

// Указываем путь к вашему словарю
val inputDictionary = file("tools/base-dictionary.txt")
// Сохраняем сгенерированный словарь, желательно в Gradle Build папку
val outputDictionary = layout.buildDirectory
      .file("outputs/mapping/generated-dictionary.txt")

// Добавляем рандомизацию заданного словаря
tasks.register("generateObfuscationDictionary") {
  outputs.file(outputDictionary)
  doLast {
    val lines = inputDictionary.readLines()
      .filter { it.isNotBlank() }
      .shuffled(Random(System.currentTimeMillis()))
    val outputDictFile = outputDictionary.get().asFile
    outputDictFile.parentFile.mkdirs()
    outputDictFile.writeText(lines.joinToString("\n"))
  }
}

// Добавляем в зависимость Релизной сборки рандомизацию словаря
// Если у вас несколько сборок - добавьте их во все необходимые
androidComponents {
  onVariants { variant ->
    if (variant.buildType == "release") {
      val assembleTask = tasks.named("assembleRelease")
      assembleTask.configure {
        dependsOn(generateObfuscationDictionary)
      }
    }
  }
}

Затем в конфигурации R8/ProGuard надо указать путь к сгенерированому словарю и использовать этот конфиг для обфускации.

# Указываем в файле конфигурации R8/ProGuard
-obfuscationdictionary build/outputs/mapping/generated-dictionary.txt
-classobfuscationdictionary build/outputs/mapping/generated-dictionary.txt
-packageobfuscationdictionary build/outputs/mapping/generated-dictionary.txt

Вы также можете взять уже готовые плагины для рандомизации словаря, например Proguard dictionary generator Gradle Plugin


Теперь вы сможете обеспечить лучшую защиту своих проектов. Рекомендую вам при сборке релизного билда также добавить сохранять сгенерированный словарь.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Используете R8/ProGuard
68.18% Да15
22.73% Нет5
9.09% Что это?2
0% Не участвую в опросе0
Проголосовали 22 пользователя. Воздержался 1 пользователь.
Теги:
Хабы:
+6
Комментарии5

Публикации

Работа

Ближайшие события