
В этой статье я постараюсь рассказать про реверс-инжиниринг приложений для android. Этот процесс несколько отличается от оного для win-приложений: здесь нет отладчика и нет ассемблера, вместо них выступают LogCat и байт-код. Как вы уже догадались, мы будем исследовать приложение с целью его взлома, или, проще говоря, «крякать».
БРИФИНГ
В качестве подопытного кролика я выбрал программу «Multi Mount SD-Card». Ее суть заключается в монтировании flash-накопителя девайса с одновременным доступом к нему системы и пользователя. Дело в том, что по умолчанию android не имеет доступа к смонтированному в данный момент накопителю. Для пользователей Eclair это не так критично, но вот пользователи Froyo+
Для начала нам нужен дистрибутив программы, чтобы было что ломать. Но где же его взять? Ведь для этого надо ее купить. Ну, или выпросить у уже купившего человека. Таким человеком стал creage с форума 4pda, который любезно расшарил купленный apk.
Итак, у нас есть дистрибутив, мы уже установили программу, запускаем… И тут бац! Видим окошко с предложением купить лицензию. Отсюда вытекает, что проверка идет через интернет, пробуем отключить — не помогает. Покупателю приложения не дают никаких ключей и логинов, что говорит нам о привязке к чему-то вроде hardware_id или google-аккаунта. Следовательно у нас есть несколько вариантов взлома: либо захардкодить в программу заведомо правильную проверяемую информацию, либо вырезать из кода все участки, проверяющие валидность лицензии. Я выбрал второй вариант, ибо он мне больше нравится.
Инструментарий
Для работы нам нужны инструменты. Я было хотел рассказывать про них по ходу дела, но все-таки передумал и решил написать про все заранее. Собственно, сегодня нам понадобится следующий список инструментов:
Введение
Прежде чем приступать, я немного объясню структуру android приложений. Каждое приложение есть файл с расширением apk, упакованный zip’ом. В нем содержатся ресурсы приложения, AndroidManifest.xml и classes.dex. Что же из себя представляет последний? Это байт-код программы, скомпилированный специально для виртуальной машины dalvik. Получить из него чистый исходный код на java нельзя, но можно получить dalvik opcodes — набор команд для виртуально машины, грубо говоря, это местный ассемблер. А еще можно превратить dex файл в jar, после чего декомпилировать его и получить более-менее читаемый код на java. Чем мы и будем сейчас заниматься.
Декомпиляция
Все манипуляции с apk мы будем проводить с помощтю утилиты Apk Manger. Это эдакий единый фронт-енд для набора библиотек, работающих с apk. Напомню, у вас должны быть установлены драйвера для девайса и включен режим USB-debugging. Приступим.
- Копируем multimount.apk в папку apk_manager\place-apk-here-for-modding и запускаем Script.bat. Если все хорошо, то появится консоль с зелеными надписями.
- Необходимо декомпилировать apk. Выбираем одноименный пункт 9. Консоль после этого не закрываем.
- Открываем multimount.apk архиватором и копируем файл classes.dex в папку dex2jar, после чего перетаскиваем его на dex2jar.bat. В Total Commander перетаскивание не работает.
- Появившийся classes.dex.dex2jar.jar открываем с помощью jd-gui. Окно пока не закрываем, оно понадобится позже.
Начало анализа
Для получения начальной информации о приложении посмотрим на его манифест. Из него можно понять, что главным активити являются настройки. Значит код, предлагающий купить приложение, находится где-то там. Еще можно заметить странную строчку в самом низу:
- <uses-permission android:name="com.android.vending.CHECK_LICENSE" />
Что же это? Как подсказывает гугл, эта строчка намекает на то, что в приложении используется LVL, то есть менеджер лицензий android. Ну что ж, это даже хорошо, ведь у нас есть документация. Почитав которую, пожно понять, что этот LVL не только управляет лицензиями, но и подвергает код обфускации, что существенно усложнит нам работу.
Ну да ладно, приступим непосредственно к анализу кода. Переключаемся на jd-gui, разворачиваем дерево, и видим три пространства имен: первое — что-то связано с рекламой, второе — набор классов LVL, третье — то, что нам надо.
Заходим в него и видим последствия обфускации. Открываем MultiMountSDCardConfigure, бегло просматриваем код. В глаза сразу бросается длинная строчка с каким-то хэшем. Это base64 public key. А вокруг него находятся и остальные строчки, проверяющие лицензию. Их нам необходимо выпилить.
- com.android.vending.licensing.h localh = new com.android.vending.licensing.h(arrayOfByte, str4, str3);
- v localv = new v(this, localh);
- m localm1 = new m(this, localv, "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg1...");
- f = localm1;
- m localm2 = f;
- j localj = e;
- localm2.a(localj);
Открываем apk_manager\projects\multimount.apk\smali\com\rafoid\multimountsdcard\widget\MultiMountSDCardConfigure.smali и видим байт-код. Прежде чем читать дальше, советую сначала просмотреть список команд. Нам необходимо найти те самые строчки и закоментить их. Вот они:
- ...
- #iput-object v1, p0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->e:Lcom/android/vending/licensing/j;
-
- #new-instance v1, Lcom/android/vending/licensing/m;
-
- #new-instance v2, Lcom/android/vending/licensing/v;
-
- #new-instance v3, Lcom/android/vending/licensing/h;
-
- #sget-object v4, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->d:[B
-
- #invoke-virtual {p0}, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->getPackageName()Ljava/lang/String;
-
- #move-result-object v5
-
- #invoke-direct {v3, v4, v5, v0}, Lcom/android/vending/licensing/h;-><init>([BLjava/lang/String;Ljava/lang/String;)V
-
- #invoke-direct {v2, p0, v3}, Lcom/android/vending/licensing/v;-><init>(Landroid/content/Context;Lcom/android/vending/licensing/n;)V
-
- #const-string v0, "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg..."
-
- #invoke-direct {v1, p0, v2, v0}, Lcom/android/vending/licensing/m;-><init>(Landroid/content/Context;Lcom/android/vending/licensing/k;Ljava/lang/String;)V
-
- #iput-object v1, p0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->f:Lcom/android/vending/licensing/m;
-
- #iget-object v0, p0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->f:Lcom/android/vending/licensing/m;
-
- #iget-object v1, p0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->e:Lcom/android/vending/licensing/j;
-
- #invoke-virtual {v0, v1}, Lcom/android/vending/licensing/m;->a(Lcom/android/vending/licensing/j;)V
- ...
Закоменчиваем, сохраняем. Вроде как, все, можно пользоваться. Осталось только скомпилить и установить.
Компиляция и установка
Для этого будем использовать уже знакомый нам Apk Manager.
- Если вы ещё не закрыли окно консоли, переключаемся на него и выбираем пункт 14. Скрипт сам скомпилит, подпишет и установит apk на девайс.
- Запускаем приложение и видим, что теперь нам не предлагают ничего покупать, но окошко с процессом проверки висит и не закрывается.
Опять анализ
Теперь нам необходимо обнаружить и удалить код, показывающий ненужный нам диалог. Переключаемся на jd-gui и находим чуть выше следующие строчки:
- rogressDialog localProgressDialog = ProgressDialog.show(this, str1, str2, 1, 0);
- b = localProgressDialog;
Теперь надо найти их в MultiMountSDCardConfigure.smali. Вот они:
- ...
- #invoke-static {p0, v0, v1, v6, v7}, Landroid/app/ProgressDialog;->show(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZZ)Landroid/app/ProgressDialog;
-
- #move-result-object v0
-
- #iput-object v0, p0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardConfigure;->b:Landroid/app/ProgressDialog;
- ...
Закоменчиваем, сохраняем, компилим, устанавливаем, запускаем. Ура! Все работает. Но после непродолжительного тестирования мы понимаем что работает не совсем все, а именно функция авто-монтирования. Переключаемся на jd-gui, открываем MultiMountSDCardWidget$UpdateService и видим следующий кривой код:
-
- if (MultiMountSDCardWidget.b.booleanValue());
- int j;
- for (int i = 0; ; j = 1)
- {
- Boolean localBoolean = Boolean.valueOf(i);
- RemoteViews localRemoteViews = MultiMountSDCardWidget.a(this, localBoolean);
- Class localClass = MultiMountSDCardWidget.a;
- ComponentName localComponentName = new ComponentName(this, localClass);
- AppWidgetManager.getInstance(this).updateAppWidget(localComponentName, localRemoteViews);
- return;
- }
-
Именно этот сервис отвечает за авто-монтирование. В самом начале мы видим какую-то проверку, в результате которой выполняется это самое монтирование. Код проверяет переменную b из главного активити, ту самую переменную, объявление которой мы закоментили, удаляя диалог. Так же мы поступим и с этой проверкой — закоментим ее к черту.
На сей раз открываем MultiMountSDCardWidget$UpdateService.smali и закоменчиваем следующие строчки:
- ...
- .method public onStart(Landroid/content/Intent;I)V
- .locals 3
-
- #sget-object v0, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardWidget;->b:Ljava/lang/Boolean;
-
- #invoke-virtual {v0}, Ljava/lang/Boolean;->booleanValue()Z
-
- #move-result v0
-
- #if-eqz v0, :cond_0
-
- const/4 v0, 0x0
-
- :goto_0
- invoke-static {v0}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
-
- move-result-object v0
-
- invoke-static {p0, v0}, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardWidget;->a(Landroid/content/Context;Ljava/lang/Boolean;)Landroid/widget/RemoteViews;
-
- move-result-object v0
-
- new-instance v1, Landroid/content/ComponentName;
-
- sget-object v2, Lcom/rafoid/multimountsdcard/widget/MultiMountSDCardWidget;->a:Ljava/lang/Class;
-
- invoke-direct {v1, p0, v2}, Landroid/content/ComponentName;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
-
- invoke-static {p0}, Landroid/appwidget/AppWidgetManager;->getInstance(Landroid/content/Context;)Landroid/appwidget/AppWidgetManager;
-
- move-result-object v2
-
- invoke-virtual {v2, v1, v0}, Landroid/appwidget/AppWidgetManager;->updateAppWidget(Landroid/content/ComponentName;Landroid/widget/RemoteViews;)V
-
- return-void
-
- #:cond_0
-
- const/4 v0, 0x1
-
- goto :goto_0
- .end method
- ...
Запускаем и радуемся, что сэкономили целых 30 рублей на покупке этой полезнейшей программы.
Примечания
Процесс описан целиком для Windows, но легко может быть повторен и на Linux, так как все необходимые библиотеки кроссплатформенны.
Уже после написания статьи я наткнулся на на вот этот способ взлома, но, насколько я понимаю, он актуален для ранних версий LVL.
Вся информация представлена исключительно в ознакомительных целях. А так же для разработчиков, с целью улучшения программной защиты.