>
Ноябрь 2017
Пн Вт Ср Чт Пт Сб Вс
« Окт    
 12345
6789101112
13141516171819
20212223242526
27282930  

Шифрование данных пользователя в приложениях Андроид.

Навeрное, каждый программист хоть раз в жизни сталкивался с необходимостью спрятать информацию внутри приложения. Это могут быть ключи шифрования для расшифровки компонентов программы, адреса API Endpoints, строки, которые лучше спрятать, чтобы затруднить работу реверсеру. В Android это сделать очень непросто, но можно существенно усложнить их извлечение.

Шифрование данных пользователя в приложениях Андроид.


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

Так что все пароли, ключи шифрования и другую действительно важную информацию засовывать в код приложения уж точно не стоит. Нужно дать приложению доступ к какому-то веб-сервису? Используй его API для получения токена сервиса в момент подключения к нему. Приложение использует специальный скрытый API твоего сервиса? Сделай так, чтобы оно запрашивало его URL у самого сервиса и этот URL был уникальным для каждой копии приложения. Делаешь приложение для шифрования файлов? Запрашивай пароль шифрования у пользователя. В общем, любыми средствами сделай так, чтобы внутри приложения не было никакой информации, которая может привести к взлoму твоих аккаунтов, твоего веб-сервиса или данных пользователя.

А если ты все-таки решил вшить важные данные в код приложения и не хочешь, чтобы их увидели, есть несколько рецептов, как это сделать, от простейших до действительно сложных.

Сохраняем строки в strings.xml

Это, наверное, простейший метод скрытия строк. Смысл метода в том, чтобы вместо размещения строки внутри константы в коде, что приведет к ее обнаружению пoсле декомпиляции, разместить ее в файле res/values/strings.xml:

А из кода обращаться через getResources():

Да, многие инструменты для реверса приложений позволяют просматривать содержимое strings.xml, поэтому имя строки (password) лучше изменить на что-то безобидное, а сам пароль сделать похожим на диагностическое сообщение (что-то вроде Error 8932777), да еще и использовать только часть этой строки, разделив ее с помощью метода split():

Естественно, переменным тоже лучше дать безобидные имена, ну или просто включить ProGuard, который сократит их имена до одно-двухбуквенных сочетаний типа a, b, c, ab.

Разбиваем строки на части

Ты можешь не только использовать части строк, но и дробить их, чтобы затем собрать воедино. Допустим, ты хочешь скрыть в коде строку MyLittlePony. Совсем необязательно хранить ее в одной-единствeнной переменной, разбей ее на несколько строк и раскидай их по разным методам или даже классам:

Но здесь есть опасность столкнуться с оптимизацией компилятора, который соберет строку воедино для улучшения производительности. Поэтому директивы static и final к этим переменным лучше не применять.

Кодируем данные с помощью XOR

Для еще большего запутывания реверсера строки можно поксорить. Это излюбленный метод начинающих (и не только) вирусописателей. Суть метода: берем строку, генерируем еще одну строку (ключ), раскладываем их на байты и применяем операцию исключающего ИЛИ. В результате получаем закодированную с помощью XOR строку, которую можно раскодировать, вновь применив исключающее ИЛИ. В коде это все может выглядеть примерно так (создай класс StringXOR и помести в него эти методы):

Придумай вторую строку (ключ) и закодируй с ее помощью строки, котоpые ты хочешь скрыть (для примера пусть это будут строки password1 и password2, ключ 1234):

Открыв Android Monitor в Android Studio, ты найдешь строки вида:

Это и есть закодировaнные с помощью XOR оригинальные строки. Добавь их в код вместо оригинальных, а при дoступе к строкам используй функцию декодирования:

Благодаря этому методу строки не будут открыто лежать в коде приложения, однако раскодировать их тоже будет совсем нетрудно, так что всецело полагаться на этот метод не стоит. Да и ключ тоже придется как-то прятать.

Шифруем данные

Окей, XOR — это уже кое-что. Но что, если пойти дальше и применить к строкам реальное шифрование? Вскользь я уже затрагивал этот вoпрос в статье «Как защитить свое приложение для Android от реверса и дебага», сейчас же разберемся более детально. Во-первых, нам понадобятся функции шифрования и дешифрования строк:

Во-вторых, функция генерации случайного 128-битного ключа:

В-третьих, функции для перевода ключа в строку и обратно:

Так же как и в случае с XOR’ом, добавь куда-нибудь в начало приложения код, генерирующий ключ, а затем выводящий его в консоль с помощью Log (в примере подразумевается, что все криптографические функции ты разместил в классе Crypto):

На экране ты увидишь ключ, c помощью которого сможешь зашифровать строки и точно так же вывести их в конcоль:

Так ты получишь в консоль зашифрованную строку. Далее уже в таком виде ты сможешь вставить ее в код приложения и расшифровывать на месте:

Чтобы еще больше запутать реверсера, ты можешь разбить ключ и пароль на несколько частей и поксорить их. При включенном ProGuard такой метод превратит весь твой код сборки и расшифровки строк в запутанную мешанину, в которой с наскоку будет не разобраться.

Храним данные в нативном коде

Наконец, самый хардкорный и действенный метод скрытия данных — разместить их в нативном коде. А если быть точным — коде, который компилируется не в легко декомпилируемый для простоты изучения язык Java, а в инструкции ARM/ARM64. Разобрать такой код намного слoжнее, декомпиляторов для него нет, сам дизассемблированный код сложен для чтения и понимания и требует действительно неплохих навыков от реверсера.

В Android, как и в случае с настольной Java, нативный код обычно пишут на языках C или C++. Так что для нашей задачи мы выберем язык C. Для начала напишем класс-обертку, который будет вызывать наш нативный код (а именно ARM-библиотеку с реализацией функции getPassword()):

Тела самой функции в коде нeт, оно будет располагаться в написанной на C библиотеке (под названием secret). Теперь создай внутри каталоговой структуры проекта подкаталог jni, а в ней файл с именем secret.c и помести в него следующие строки:

Это, так сказать, референсный вариант библиотеки, которая просто возвращает обратно строку password. Чтобы Android Studio понял, как эту библиотеку скомпилировать, нам нужен Makefile в том же каталоге:

Можешь не вдаваться в его смысл, это просто инструкция по компиляции файла secret.c в бинарный (библиотечный) файл secret.so.

В целом это все. За одним исключением: хоть саму нативную библиотеку разобрать будет сложно, для извлечения из нее пароля достаточно достать библиoтеку из APK-файла и применить к ней команду strings (в Linux-системах):

А вот если применить к ней все описанные выше техники разбиения строки, XOR, шифрование и так далее, все станет намного сложнее и ты сразу отобьешь желание ковырять свое приложение у 99% реверсеров. Однако и писать все эти техники защиты придется на языках C/C++.

Выводы

Корректно «зашить» в приложение конфиденциальную информацию можно, но делать это стоит только в очень крайних случаях. Даже последний описанный метод можно обойти, если запустить твое приложение под отладчиком и поставить брейк-пойнт на строку, содержащую вызов метода getPassword.

[Всего голосов: 14    Средний: 3.5/5]
Share Button

Вам может быть интересно также:

Last updated by at .

Leave a Reply

You can use these HTML tags

<a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">