Взлом одного Android приложения

    Недавно я усиленно разрабатывал свое приложение под Android, и в процессе защиты платной версии понял, что довольно сложно обезопасить приложение от взлома. Ради спортивного интереса решил попробовать убрать рекламу из одного бесплатного приложения, в котором баннер предлагается скрыть, если заплатить денежку через In-App Purchase.


    В этой статье я опишу, как мне удалось убрать рекламу бесплатно и в конце — несколько слов о том, как усложнить задачу взломщикам.

    Шаг 1. Получаем «читаемый» код приложения.
    Чтобы добыть APK приложения из телефона, нужны root права. Вытягиваем приложение из телефона с помощью adb (пусть, для конспирации, у нас будет приложение greatapp.apk):
    adb pull /data/app/greatapp.apk

    Хабраюзер overmove подсказал мне, что root необязателен, можно с помощью Astro сделать бэкап любого приложения, и оно будет скопировано в /mnt/sdcard.
    Хабраюзер MegaDiablo подсказал мне, что и Astro необязателен. Список установленных приложений и их файлы apk можно узнать через утилиту pm в шелле, а когда уже известно имя файла, его можно стянуть через adb pull /data/app/app.filename.apk.

    APK — это ZIP архив, достаем оттуда интересующий нас файл classes.dex со скомпилированным кодом.
    Будем использовать ассемблер/дизассемблер smali/baksmali для наших грязных дел.
    java -jar baksmali-1.3.2.jar classes.dex

    На выходе получаем директорию out с кучей файлов *.smali. Каждый из них соответствует файлу .class. Естественно, все обфусцированно по самое не хочу, выглядит эта директория вот так:


    Попытаемся понять, где в этой обфусцированной куче «говорится» о рекламе. Сначала я просто сделал поиск с текстом "AdView" (View, отображающий рекламу из AdMob SDK) по всем файлам. Нашелся сам AdView.smali, R$id.smali и некий d.smali. AdView.smali смотреть не очень интересно, R.$id я как-то сначала проигнорировал, и пошел сразу в таинственный d.smali.

    Шаг 2. Пойти по неверному пути.
    Вот и метод a() в файле d.smali с первым упоминанием AdView (я решил, скриншотом лучше, а то без форматирования это очень уныло читать):


    Метод ничего не возвращает, поэтому я, недолго думая, решил просто вставить поближе к началу return-void. Когда я все собрал и запустил, приложение радостно крэшнулось. Лог из adb logcat:

    E/AndroidRuntime(14262): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.greatapp/com.greatapp.GreatApp}: android.view.InflateException: Binary XML file line #22: Error inflating class com.google.ads.AdView

    Понятно, что наш AdView в результате манипуляций должным образом не создался. Забудем пока про d.smali.

    Шаг 3. Откатываем назад все изменения и смотрим на пропущенный ранее R$id. Вот и строчка с AdView:
    # static fields
    .field public static final adView:I = 0x7f080006


    Похоже, это идентификатор View с рекламой. Поищем, где он используется, сделав поиск по значению 0x7f080006. Получаем всего два результата: тот же R$id и GreatApp.smali. В GreatApp.smali текст уже гораздо интереснее (комментарии мои):


    Видно, что этот идентификатор используется для поиска View (строка 588) и буквально сразу же AdView удаляется с экрана (строка 595). Видимо, удаляется, если пользователь заплатил за отсутствие рекламы? Если посмотреть немного выше, то взгляд цепляется за строчку 558 с «ключевыми словами»:
    invoke-static {v7, v8}, Lnet/robotmedia/billing/BillingController;->isPurchased(Landroid/content/Context;Ljava/lang/String;)Z

    robotmedia — сторонняя (open source) библиотека, призванная упростить работу с in-app billing-ом в андроиде. Почему же она не была полностью обфусцирована? Ну да ладно, повезло.
    Видно, что метод isPurchased() возвращает строку, которая с помощью Boolean.valueOf() преобразуется в объект Boolean и, наконец, в обычный boolean через booleanValue().
    И тут самое интересное, в строке 572 мы переходим в некий :cond_32, если значение результата == false. А иначе начинается уже просмотренный код поиска и удаления AdView.

    Шаг 4. Минимальное изменение, собрать и запустить.
    Что ж, дело за малым — удаляем эту ключевую строку, собираем приложение и сразу инсталлируем на телефон:
    java -jar ..\smali\smali-1.3.2.jar ..\smali\out -o classes.dex
    apkbuilder C:\devel\greatapp\greatapp_cracked.apk -u -z C:\devel\greatapp\greatapp_noclasses.apk -f C:\devel\greatapp\classes.dex
    jarsigner -verbose -keystore my-release-key.keystore -storepass testtest -keypass testtest greatapp_cracked.apk alias_name
    adb install greatapp_cracked.apk


    (greatapp_noclasses.apk — это оригинальный APK приложения, из которого удален classes.dex, сертификаты создаются с помощью Android SDK).
    И ура, запускаем приложение, никакой рекламы!

    Теперь о том, как усложнить задачу любителям халявы (это лишь то, что я запомнил из видео про пиратство с Google IO 2011, ссылка ниже):
    • Не осуществлять проверку оплаты или лицензирования в классах Activity и особенно методах onCreate() и ему подобных. Эти «точки входа» запускаются всегда в известное время и не обфусцируются, их всегда можно посмотреть и понять, что происходит с различными элементами UI
    • Лучше всего проводить проверку не в основном потоке и в случайные моменты времени
    • Проверять CRC файла classes.dex, причем хранить его зашифрованным
    • Хранить код проверки лицензии или покупки скомпилированным и зашифрованным как ресурс приложения, динамически его загружать и запускать через reflection


    Надеюсь, было интересно. В заключение, несколько полезных ссылок по теме:
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 42

        +2
        Если приложение сделано грамотно, то оно не будет работать с AdFree, например как SSHDroid. Оно проверяет показ рекламы и при запуске предлагает купить полную версию, либо восстановить файл hosts.
          +24
          Приложение, которые восстанавливает мой hosts без моего разрешения должно быть удалено как троянское.
            +6
            вообще-то я написал, что оно предлагает это сделать (с целью включения адс и соответственно работы программы)
            • UFO just landed and posted this here
                +1
                Да не требует она ничего, а просто не работает с модифицированным hosts, прямо намекая: «не поправишь — не поеду».
            +2
            Скоро его сольют в Google Play. Уже столько единиц из-за этого новшества наставили ему
              +10
              Грустно, конечно. То есть хорошему приложению ставят низкий бал не за то, что оно плохо работает, а за то, что не получилось его «взломать» (хотя я предполагаю, что очень просто найти взломанную версию, если задаться целью).

              Я понимаю, если бы оно стоило каких-то заоблачных денег; но 46 рублей, меньше полу процента от стоимости среднего android-телефона.

              Что характерно, те, кто ставят 1 ещё что-то там про жадность пишут.
                +21
                Не согласен. Это не грустно. Это ужасно, это катастрофа. Я бы еще понял если б приложение стоило от 500 рублей — это уже ощутимая сумма для людей. Но 46 рублей!!! Если ты пользуешься каждый день приложением за 46 рублей и жмешь эту сумму — ты моральный урод. Я так считаю.
                Если приложение которым я пользуюсь стоит меньше 100 рублей — покупаю сразу.
                Но перед этим, конечно, или ставлю лайт версию, или ставлю пиратку :) Но тут уж не денешься ни куда.
                  0
                  Это Андроид, здесь не принято платить.
                  +8
                  Очень не люблю читать отзывы о моем бесплатном приложении. Обычно там самые гадкие комментарии. Одному не нравится то, что нравится другому. Делаешь нечто компромиссное — ставят колы в итоге оба. Гораздо приятнее развивать платную версию приложения. Обычно приобретают приложение адекватные люди. Даже новые функции добавить просят, а не требуют. Парадокс.
                    +1
                    Да, тоже замечал, такое ощущение, что это два разных маркета (сам не разработчик, просто наблюдение пользователя)
                      +4
                      Я просто эти комментарии минусую. Задолбали быдло-неадекваты, пишущие отзывы с сотней орфографических и пунктуационных ошибок, мол, у миня нирапотайет автор казел.
                        +2
                        (сам айфонщик) а что, на Google Play можно рейтинг выставлять комментам как на хабре?
                          +4
                          Да, а ещё помечать как спам.
                            +1
                            С недавних пор там можно комменты сортировать по рейтингу, фильтровать по устройствам. То есть хочешь видеть отзывы только по своему аппарату — пожалуйста, хочешь, чтобы бесполезный спам не попадался на глаза — сортируй по рейтингу.
                              +2
                              Сам рейтинг в цифрах не виден, но можно отметить коммент как Helpful или Unhelpful и по этому Helpfulness сортировать.
                      0
                      А как это делается? Как проверяется, что реклама показывается? Что если подключения к интернету нет?
                        0
                        Видимо проверяется содержимое /etc/hosts и если там over9000 записей, прога решает, что установлен AdFree
                    +8
                    Для анализа кода андроидных приложений неплохо подходит связка dex2jar + jd-gui. Правда, собрать приложение после этого назад, скорее всего, не получится.
                      0
                      Да, удобно смотреть код в jd-gui (после dex2jar) — можно локализовать нужное место, а патчить с backsmali/smali
                      0
                      > Проверять CRC файла classes.dex, причем хранить его зашифрованным

                      Это возможно? (Я дилетант, но подозреваю, что приложение может не иметь возможности доступа к своему же байт-коду)
                      +9
                      1. Качаем приложение с баннерной рекламой (например Angry Birds)
                      2. Выключаем Wi-Fi и 3G
                      3. Запускаем приложение
                      4.…
                      5. PROFIT

                      Да для приложений которым нужна сеть (например ssh клиент этот метод не сработает)
                        0
                        можно просто заблокировать приложение в Droidwall
                          0
                          Я так и делаю. Правда, в iOS.
                            0
                            Да, с Angry Birds я тоже так делал :)
                            Но:
                            1) Несколько напрягает включать-выключать интернет
                            2) Приложению может быть нужен интернет для нормальной работы
                            3) Место для рекламы все равно будет съедаться, хотя зависит от приложения, в Angry Birds это не так.
                              0
                              1. Нисколько не напрягает. Не отключённый интернет скушивает батарею слишком быстро/грустно. Постоянные нотификации нисколько не радуют. Хотя да, если нужно постоянно быть в курсе почты/чего-либо ещё, то не вариант
                              2. У меня такое подозрение что действительно нужна сеть скорее рабочим приложениям, которые не грех и купить
                              3. Не видел таких
                                +2
                                По третьему пункту — я как-то в одном бесплатном приложении предусмотрел такой вариант: если баннер не получается загрузить из сети, тогда вместо него просто показывается черный прямоугольник с надписью Advertisment (точнее, прямоугольник — это вьюха, в которую помещается баннер).
                                Гадкий я )
                                  +1
                                  лучше бы написали «программирую за еду», а лучше какую-нибудь шутку/цитату… глядишь кто-нибудь прочувствовался бы и заплатил ;)
                                    +2
                                    А неплохая мысль, кстати, цитаты показывать там…
                            –3
                            etc/hosts
                              0
                              Если у вас есть рут, то многие кастомные прошивки (либо установка соответствующей программы) позволяют редактировать разрешения для приложений — и им можно просто запретить вылезать в интернет.
                              Или обрезать доступ в инет каким-либо фаерволом ;)
                              p.s. Естественно это применимо для приложений которым не нужен интернет для работы
                              • UFO just landed and posted this here
                                  –3
                                  Если у вас есть рут и вы не разработчик, то в перспективе вы клиент антивирусной компании. Они вас ждут :)
                                  0
                                  Вставлю свои 5 копеек про упрощение получения исходного кода. AirDroid позволяет получить dex-файл без наличия root, не для всех приложений, но для большинства.
                                    +1
                                    Не хочу ни кого обидеть, но RTFM. Ребята у вас есть под рукой такая простая и замечательная утилита и имеет она название adb. По личному опыту могу сказать нет такого приложения которое нельзя было бы скачать с приложения и для этого не надо иметь root доступа.
                                    +6
                                    Статья понравилась.
                                    Любителям халявы:
                                    если автор приложения сам не раздаёт его бесплатно и без рекламы, значит это ему (автору) зачем-то надо.
                                    И, если пользователи будут игнорировать условия распространения приложения, определенные автором, скорее всего автор потеряет интерес к приложению и через какое-то время забросит его. Или будет уделять меньше времени и усилий на разработку/обновления.

                                    Вам это надо?

                                    Нет денег, не хотите платить, не любите рекламу — не используйте такое приложение или (вместо того чтобы ломать защиту) напишите своё и раздайте другим.
                                    Поверьте, создавать что-то своё гораздо интереснее, чем копаться в чужом коде.
                                      0
                                      Не согласен с последним утверждением. Иногда интереснее покопаться где-то (ессно, не в CD-ejector'e).
                                      +5
                                      Столько стараний за 99¢ (обычно)!
                                      Но для разработчика полезные знания. Спасибо.
                                        +1
                                        не проще было использовать dex2jar и Java Decompiler?)
                                          +2
                                          А мне кажется бороться с пиратами бесполезно. Тут все от политики монетизации зависит, если все правильно сделать то крякеры лишь помогают разработчику.

                                          Only users with full accounts can post comments. Log in, please.