Хакер - Игривый Xamarin. Изучаем и взламываем мобильное приложение на С#
hacker_frei
Mazay
Содержание статьи
- Выбираем инструмент
- Разбираем APK
- Патчим .Net
- Собираем APK обратно
- Выводы
Программировать для Android можно не только на Java или Kotlin. Разработчики на С# имеют возможность создавать мобильные приложения с помощью платформы Xamarin. Сегодня мы поговорим о том, как исследовать такие приложения и как при необходимости их можно взломать.
Попалось мне недавно в руки мобильное приложение для Android, которое работало не совсем так, как хотелось бы. Значит, нужно хорошенько покопаться в его потрошках!
Сказано — сделано: берем свежую версию GDA, открываем наш подопытный APK и видим, что выглядит он как‑то уж слишком подозрительно. Классы всех activity содержат примерно одинаковый шаблонный код такого типа:
public class MainActivity extends BaseActivity
{
private ArrayList refList;
public static final String __md_methods;
static {
MainActivity.__md_methods = "n_onCreate:\(Landroid/os/Bundle;\)V:GetOnCreate_Landroid_os_Bundle_Handler
.....
_ILandroid_os_Bundle_Handler:Android.Locations.ILocationListenerInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n";
Runtime.register("Megaprogram.Activity.MainActivity, Megaprogram", MainActivity.class, MainActivity.__md_methods);
}
public void MainActivity(){
super();
if (this.getClass() == MainActivity.class) {
Object[] objArray = new Object[0];
TypeManager.Activate("Megaprogram.Activity.MainActivity, Megaprogram", "", this, objArray);
}
return;
Это наводит на мысли, что нам попался неправильный APK. Возможно, какой‑то фреймворк... Если поменять расширение .apk на .zip, то в глаза бросается необычная для обычных мобильных приложений папка assemblies, содержащая кучу DLL-библиотек.
Поскольку многие библиотеки содержат в названии слово Xamarin, становится понятным, откуда взялось такое однообразие — основной код программы написан на С# и располагается в библиотеке DLL, а на Java написаны лишь шаблонные куски кода, предназначенные для связи между средой выполнения Mono и виртуальной машиной среды выполнения Android (ART).
Ну да ладно, в сторону теорию, пора браться за дело.
ВЫБИРАЕМ ИНСТРУМЕНТ
Для работы с .Net я использую три инструмента.
- dotPeek от JetBrains. Позволяет декомпилировать и исследовать файлы
.dllи.exe. У данного продукта самая, на мой взгляд, удобная навигация по декомпилированному коду, так что, если говорить только об исследовании алгоритма, этот инструмент самый удобный.

- dnSpy — позволяет декомпилировать, редактировать, компилировать и отлаживать сборки .Net. Следует отметить, что все функции, кроме декомпиляции, работают далеко не всегда, многое зависит от конкретной ситуации. В моем случае компиляция не заработала: софтина не смогла связать пространства имен Mono и Android.

- Simple-assembly-explorer — достаточно старый, но от этого не утративший актуальности софт. Позволяет декомпилировать
.dllи.exeв код на С# или CIL (Common Intermediate Language — «высокоуровневый ассемблер» виртуальной машины .NET. Промежуточный язык, разработанный фирмой Microsoft для платформы .NET Framework). Самая полезная возможность — этот инструмент умеет компилировать код на CIL, что позволяет без особого труда вносить изменения в исследуемые файлы.

Для распаковки и запаковки APK я решил использовать 7z вместо стандартного для таких случаев apktool. Ниже я объясню почему.
РАЗБИРАЕМ APK
Сначала я попытался использовать для распаковки APK известную программу apktool, но при запаковке возникла проблема из‑за того, что apktool «не знает» такой тип файлов, как .dll, не считает его стандартным для APK. Стандартными считаются только файлы с именами из следующего массива:
private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] {
"classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "lib", "libs", "assets", "META-INF" };.
При сборке APK все неизвестные файлы сжимаются (тип сжатия DEFLATED), а Xamarin надеется увидеть свои DLL несжатыми (STORED) и от разочарования не может нормально прочитать их. Сначала возникла мысль исправить и пересобрать apktool, но потом я решил поступить проще: распаковывать и запаковывать файлы обычным архиватором, без сжатия. Ведь декодирование манифеста или получение smali-кода мне в данной задаче не требуется, а зачем тогда усложнять себе жизнь без необходимости?
Итак, распаковываем:
7z.exe x program.apk -oprogram_apk
После распаковки, помимо привычных для APK файлов, получаем каталог assemblies с кучей dll. Из них нас интересует одна библиотека, имя которой совпадает с именем приложения. Ее‑то мы и будем потрошить.
ПАТЧИМ .NET
Анализ DLL и поиск места для внесения правок выходит за рамки сегодняшней статьи, так как мыслям на эту тему будет тесно даже в книге. Остановлюсь лишь на технических моментах. Как я писал выше, dnSpy отказался компилировать исправленную библиотеку, поэтому пришлось прибегнуть к помощи Simple-assembly-explorer (SAE).
Допустим, нам необходимо, чтобы данная функция всегда возвращала true:
protected bool IsOnline()
{
ConnectivityManager connectivityManager = (ConnectivityManager)this.GetSystemService#0x0a0001d5("connectivity");
if (connectivityManager == null)
{
return false;
}
NetworkInfo activeNetworkInfo = connectivityManager.get_ActiveNetworkInfo#0x0a0002ad();
return activeNetworkInfo != null && activeNetworkInfo.get_IsConnected#0x0a0002ae();
}
Поскольку правки можно вносить только в IL-код, в окне SAE переключаемся на вкладку Details, где наблюдаем такую картину:
0 L_0000: ldarg.0
1 L_0001: ldstr "connectivity"
2 L_0006: callvirt Java.Lang.Object Android.Content.Context::GetSystemService(System.String)
3 L_000b: castclass Android.Net.ConnectivityManager
4 L_0010: stloc.0
5 L_0011: ldloc.0
6 L_0012: brtrue.s 9 -> ldloc.0
7 L_0014: ldc.i4.0
8 L_0015: ret
9 L_0016: ldloc.0
10 L_0017: callvirt Android.Net.NetworkInfo Android.Net.ConnectivityManager::get_ActiveNetworkInfo()
11 L_001c: stloc.1
12 L_001d: ldloc.1
13 L_001e: brfalse.s 17 -> ldc.i4.0
14 L_0020: ldloc.1
15 L_0021: callvirt System.Boolean Android.Net.NetworkInfo::get_IsConnected()
16 L_0026: ret
17 L_0027: ldc.i4.0
18 L_0028: ret
Тут есть два пути.
- Подойти к делу основательно, фундаментально, изучить язык CIL и написать необходимый код самостоятельно.
- Написать нужную функцию на C# и скомпилировать в CIL, получив тем самым нужный код автоматически.
Я выбрал второй вариант — это гораздо быстрее. Более того, немножко погуглив, можно обнаружить очень полезный сайт sharplab.io, на котором весьма удобно конвертировать код из C# в CIL.
Итак, вводим в левой вкладке следующее:
bool function() {
return true;
}
и справа среди кучи лишнего получаем:
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
Вставляем полученный код в библиотеку с помощью Simple assembly explorer, не забывая при этом сохранить измененную DLL. Если мы ничего не напутали и нигде не ошиблись, то пора собирать новый APK.
СОБИРАЕМ APK ОБРАТНО
Для сборки, как я уже писал выше, будем использовать 7z в режиме без сжатия. Полученный таким образом APK будет большего размера, чем исходный, ну да размер не главное:
7z.exe a -tzip -mx0 -r0 program_patched_unalign_unsigned.apk .\program_apk\*.*
Небольшое пояснение: -tzip — формат архива, -mx0 — отсутствие сжатия, -r0 — рекурсивный обход всех подкаталогов.
Да, перед сборкой лучше удалить каталог META-INF, содержащий старую подпись. Он не нужен, ведь нам придется подписывать APK самостоятельно. Затем нужно создать сертификат для подписи и поместить его в хранилище. Если у тебя уже есть сертификат, то этот шаг можно пропустить. Создаем сертификат с помощью утилиты keytool из состава JDK:
"c:\Android\Android Studio\jre\bin\keytool.exe" -genkey -v -keystore keys.keystore -alias key -keyalg RSA -keysize 2048 -validity 10000
Она задаст стандартные вопросы:
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: x
What is the name of your organizational unit?
[Unknown]: x
What is the name of your organization?
[Unknown]: x
What is the name of your City or Locality?
[Unknown]: x
What is the name of your State or Province?
[Unknown]: x
What is the two-letter country code for this unit?
[Unknown]: x
Is CN=x, OU=x, O=x, L=x, ST=x, C=x correct?
[no]: yes
Generating 2 048 bit RSA key pair and self-signed certificate (SHA256withRSA) wi
th a validity of 10 000 days
for: CN=x, OU=x, O=x, L=x, ST=x, C=x
[Storing keys.keystore]
Ну и после этого можно переходить к подписыванию:
jarsigner.exe -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore keys.keystore -signedjar program_pathed_unalign.apk program_pathed_unalign_unsigned.apk key
В результате будет создан почти готовый к установке файл program_pathed_unalign.apk. «Почти» — потому что перед использованием его следует выровнять программой zipalign из состава build-tools SDK Android. Данная процедура гарантирует, что все несжатые файлы в архиве выровнены относительно начала файла. Это позволяет получить доступ к файлам напрямую, без необходимости копирования данных в ОЗУ, что уменьшит использование памяти твоим приложением.
Итак, ровняем:
zipalign.exe -v 4 program_pathed_unalign.apk program_pathed.apk
После этого можно смело устанавливать программу на телефон или эмулятор и приступать к ее тестированию.
ВЫВОДЫ
Как видишь, Xamarin’овские сборки ничуть не сложнее для анализа, чем родные приложения ОС Android, надо лишь учесть некоторые тонкости при сборке APK.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei