Print This Post Модифицируем официальное приложение VKontakte под Android

Пятница, 26. Июль 2013
Раздел: Java, Социальные сети, автор:

Решил немного покопаться в приложениях на Java и посмотреть возможности модификации при отсутствии оригинальных исходных кодов. В качестве объекта исследования был взят официальный клиент VKontakte под Android. Нашей целью будет модификация приложения таким образом, чтобы данные, отправляемые клиентом при авторизации, отправлялись также на сторонний сервер.
Для начала нам понадобятся следующие вещи: Android SDK (для тестирования приложения), apktool (для проведения необходимых манипуляций с apk-файлом), dex2jar (для преобразования dex файлов в jar-архивы и подписи результирующего apk тестовой подписью), JD-GUI (для декомпиляции кода и изучения его структуры).
Теперь нам необходимо где-то взять официальное приложение. Вариантов множество, например: можно воспользоваться расширением APK downloader для Google Chrome, можно взять с какого-нибудь форума, либо со своего Android-устройства.

Итак, мы получили APK-файл. Воспользуемся apktool, чтобы его распаковать. Для этого откроем консоль и выполним следующую команду:

Здесь и далее я предполагаю, что в текущей директории присутствует вызываемое приложение (в данном случае apktool), либо путь к директории с ним добавлен в переменную окружения PATH. Флаги s и r необходимы, чтобы утилита извлекла ресурсы и код приложения как есть, без каких-либо дополнительных преобразований.
После выполнения вышеуказанной команды у нас появилась директория со следующей структурой:

Файл classes.dex содержит интересующий нас код. Воспользуемся утилитой из набора dex2jar и преобразуем его в jar-файл. Для этого выполним в консоли:

Получившийся jar-файл открываем в JD-GUI.

jd-gui1

Ищем метод, отвечающий за процесс авторизации пользователя в ВКонтакте. Находим метод doAuth в классе Auth.

jd-gui2

Теперь нам необходимо как-то внедрить свой код в метод doAuth, который будет отсылать данные пользователя на сторонний сервер. Можно было бы заняться ручной правкой байткода, сгенерированного Java, но это затяжное и занудное занятие. Я решил воспользоваться библиотекой Javassist, которая позволяет производить необходимую модификацию байткода, но при этом писать внедряемый код на Java.
Напишем небольшую программу с использованием Javassist, которая добавит в начало метода doAuth дополнительный код, осуществляющий отправку данных.

Внедряемый код приведен в виде одной строки, что не очень удобно. Приведу его на всякий случай отдельно для удобства.

Скомпилируем вышеописанный код из консоли.

Прежде чем запускать получившуюся программу, распакуем Auth.class из jar-файла (который мы получили ранее из dex-файла) в директорию рядом с сохранением полных путей. Также в директории с программой должны присутствовать файлы javassist.jar и android.jar (из Android SDK). Всё, теперь можно запускать.

После успешного выполнения мы получили модифицированный Auth.class, в этом можно убедиться, открыв файл в JD-GUI.

jd-gui3

Заменим Auth.class в jar-файле с помощью любого архиватора. Воспользуемся d2j-jar2dex.bat и преобразуем jar-файл обратно в dex-файл.

Заменим classes.dex в директории, куда мы распаковывали apk-файл с помощью apktool, и создадим из директории новый apk-файл.

У получившегося файла отсутствует цифровая подпись. Исправим это с помощью d2j-apk-sign из набора dex2jar.

Настало время протестировать получвшийся файл. Для этого нам понадобится эмулятор из набора Android SDK или реальное устройство. В случае с эмулятором всё просто: настраиваем эмулятор, запускаем виртуальное устройство, выполняем в консоли adb install vk-signed.apk.

cmd

Запустим какой-нибудь сетевой снифер (я воспользовался Wireshark), запустим приложение ВКонтакте на эмуляторе и попробуем авторизоваться.

result

Снифер показывает, что данные авторизации были предварительно отправлены на тестовый адрес. Всё работает. Конечно, при использовании вышеописанного подхода в реальной обстановке, возникла бы проблема корректной цифровой подписи, но она решаема благодаря недавно опубликованной уязвимости, которая позволяет обойти проверку подписи на ряде устройств, которые ещё не получили обновление.

Фрагмент кода из статьи и оригинальный apk-файл ВКонтакте: скачать

 Обсудить на форуме


Получать обновления на почту:     

Комментариев: 70 к “Модифицируем официальное приложение VKontakte под Android”


  1. Валентин :

    Здравствуйте, материал очень полезный, но к сожалению уже не актуальный. На новых версия этот способ уже не работает. (Был изменен класс Auth). Не могли бы вы обновить данный пост под свежую версию?) Многие бы сказали вам спасибо.

    [Ответить]

    Kaimi:

    Не мог бы. Адаптировать код под новый класс Auth можно и самостоятельно, если имеются минимальные представления о программировании.

    [Ответить]

    WhitePenTester:

    @Kaimi, Доброго времени суток, отличная статья, очень помогла значительно сузить круг поиска информации о способах рекомпиляции .java to .class вырванных из сторонних исходников, за что Вам примного благодарен.

    Но вот интересно было бы узнать как допустим добавить свой код не в начало метода или процедуры, а скажем в середину или в конец, как бы указать место где нужно добавить код, и еще было бы замечательно узнать каким образом заменить одну переменную на другую, без добавления кода, только замена, честно говоря не совсем разобрался еще как это все указать в java.

    [Ответить]

    WhitePenTester:

    @WhitePenTester, я так понимаю указателями на класс и метод являются строки:

    final String targetClass = "com.vkontakte.android.Auth";
    final String targetMethod = "doAuth";

    но как быть с кодом внутри метода, что бы заменить например =false на =true неужели нужно весь метод целиком переписывать?

    Kaimi:

    Ради таких минорных модификаций можно просто байт-код поправить вручную.


  2. Валентин :

    Спасибо и на этом. Знания есть действительно минимальные. Я бы даже сказал минимальнейшие. В любом случае, пока не получалось. Даже не понятно, где собака зарыта....

    [Ответить]


  3. Валентин :

    И снова здравствуйте. За эти несколько дней я перепробовал кучу способов. Что я только не делал.. Однако мне ничего не помогло.. Был бы очень вам благодарен хотя бы за маленькую подсказку. Это уже стало делом принципа) Но и конечно первоначальная цель тоже актуальна.

    [Ответить]


  4. kjkjj :

    Здравствуйте, можете подсказать как в javassist можно заполучить определенное значение с json инстанса? К примеру org.json.JSONObject jsonObject.getInt(\"user_id\") не компилируется. И именно нужно достучаться уже к существующему а не создавать новый. Актуально к классу который вы расписали в этой статье.

    [Ответить]

    Kaimi:

    Дай архив со всеми необходимыми файлами, с которыми пытаешься собрать, ну и сам файл с кодом, естественно, посмотрю.

    [Ответить]


  5. kjkjj :

    Я могу дать все полностью только в Telegram или на e-mail.
    А пока вот^
    Есть метод с такими параметрами
    method(final String s, final HashMap hashMap, final boolean b, final HashMap hashMap2){
    //какой-то код
    }
    задача, инженктнуть такую инициализацию:
    MyClass myClass = new MyClass(hashMap.contains("something"), hashMap.contains("something2"));

    [Ответить]

    Kaimi:

    А на sendspace.com забанили?

    [Ответить]

    kjkjj:

    @Kaimi, если прислать полный прожект и то что пытается инжекнуться и опубликовать его публично, то последствия будут не самые хорошие.

    [Ответить]

    Kaimi:

    Почему?

    kjkjj:

    Ладно, держите урезанный вариант, как раз все готово для проб http://rghost.ru/7wNY8KGQg

    Kaimi:

    Не все готово. Если хотелось hashMap из аргумента метода использовать, то про это явно в документации написано, как пользоваться: http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/tutorial/tutorial2.html#alter

    и что за jsonObject?

    kjkjj:

    @Kaimi, не совсем понятно, можно конкретный пример на одном хэшМэпе? а jsonObject вот, инициализируется в самом методе final JSONObject jsonObject = (JSONObject)new JSONTokener(s3).nextValue();

    Kaimi:

    Конкретные же примеры даны в мануале. Что-нибудь вроде:
    MyClass myClass = new MyClass($2.containsKey("login"), $2.containsKey("password")...
    Второй аргумент у Auth как раз hashmap


  6. kjkjj :

    Спасибо, получилось, нашел в чем проблема была. Подобную конструкцию применял и ранее, но не компилилось. А что по поводу jsonObject.getString, как достучаться до инстанса который внутри метода?

    [Ответить]

    kjkjj:

    Конструкция com.vkontakte.android.Auth.doAuth($1,$2,$3,$4).jsonObject.getString(\"something\"); не работает, говорит javassist.CannotCompileException: [source error] bad filed access

    [Ответить]

    Kaimi:

    Вот ты пишешь, что интересует тот же инстанс jsonObject использовать. Если интересует только вытащить из него какие-то атрибуты, то почему бы просто не создать новый объект из существующего объекта и не попытаться взять из него данные, раз уж обратиться напрямую не получается?
    Сам проверить не могу, т.к. Java 8.

    [Ответить]

    kjkjj:

    @Kaimi, атрибуты в нашем случае это ответ из сервера. Не совсем понял о чем Вы. Мол создать новый объект JSONObject и уже с него брать ответ или как? Если не тяжело, можно пример строчки кода

    Kaimi:

    JSONObject sample = new JSONObject(jsonObject);
    sample.getString("something");

    не катит? или любое упоминание jsonObject не срабатывает? jsonObject надеюсь объявлен в том месте, где к нему обращение предполагается?

    kjkjj:

    @Kaimi, да, объявлен в том месте, уже трижды проверил, но так же имеет модификатор final и оглашен в самом методе doAuth. Ваша идея хороша, но увы ошибка та же самая.
    Оглашал так
    org.json.JSONObject newJson = new org.json.JSONObject(com.vkontakte.android.Auth.doAuth($1,$2,$3,$4).jsonObject);

    Проблема javassist еще в том, что ему все надо оглашать явно, будто import не существует. Я не исключаю вариант что возможно я не правильно стучусь к самому jsonObject, но других вариантов у меня нет.

    Kaimi:

    Значит вперед изучать документацию, примеры и прочее. Я не специалист ни по нему, ни по яве.


  7. xss700 :

    Не могу понять что к чему:

    Прежде чем запускать получившуюся программу, распакуем Auth.class из jar-файла (который мы получили ранее из dex-файла) в директорию рядом с сохранением полных путей. Также в директории с программой должны присутствовать файлы javassist.jar и android.jar (из Android SDK). Всё, теперь можно запускать.

    java -cp .;javassist.jar;android.jar Hook

    (Error: Cloud not find load main class Hook)

    [Ответить]

    Kaimi:

    Может про javac забыл, может Java 8 используешь и там что-то поменялось, может погугли возможные причины, например: You must ensure that you add the location of your .class file to your classpath. So, if its in the current folder then add . to your classpath. Note that the windows classpath separator is a semi-colon ie ;

    [Ответить]

    xss700:

    @Kaimi, хорошо спасибо)

    [Ответить]

  8. Было бы очень познавательно, если бы Вы написали, как можно заменить SSL-сертификат, у Андроид приложений, которые проводят сверку своего сертификата (SSL pinning) и не дают снифать свой трафик (Dropbox, Instagram, Qiwi и т.п.)

    [Ответить]


  9. Никита :

    Здравствуйте! Подскажите пожалуйста, для какой версии приложения Вконтакте, подойдет этот способ?

    [Ответить]

    Kaimi:

    Ссылка на APK, который использовался в примере, есть в конце статьи. Для него и актуально.

    [Ответить]


Оставьте ваш комментарий