4 сентября 2012 в 12:15

Отладка приложений для Android без исходного кода на Java из песочницы tutorial

О чем статья


В этой статье я сжато, без «воды», расскажу как отлаживать приложения для Android не имея их исходного кода на Java. Статья не совсем для новичков. Она пригодиться прежде всего тем кто более или менее понимает синтаксис Smali и имеет некоторый опыт в reversing engineering приложений для Android.

Инструменты


Нам понадобится Apk-tool 1.4.1 и NetBeans 6.8. Да-да, именно эти древние версии! С более новыми версиями отладка к сожалению «не заводится», о чем уже неоднократно упоминалось в различных обсуждениях (см. например тут и тут).

На установке подробно не останавливаюсь. NetBeans устанавливается по умолчанию, просто запускам инсталляцию и кликаем Next-Next-Next. Установка Apk-tools заключается в обычной распаковке файла apktool.jar в любую папку.

Также по ходу дела нам понадобится DDMS — он есть в Android SDK.

Отладка


В этом разделе дана пошаговая инструкция. Она писалась для Windows, но вероятно сработает и на Linux и Mac OS. Черновик этой инструкции на английском также есть у меня в блоге, но ссылку не дам, ибо правила. Эта инструкция более или менее повторяет оригинальную инструкцию с Apk-tool wiki, однако содержит некоторые дополнительные пункты, без которых отладка может «не завестись». В своё время у меня не заводилась, и я нашел эти дополнительные пункты методом усиленного гугления и «научного тыка». Надеюсь, теперь мой опыт кому-то сэкономит время.

Пожалуйста, следуйте инструкции в точности – это важно!

  1. Декодируйте свой .apk файл в директорию out с помощью Apk-tool. Для этого используйте опцию -d:
    java -jar apktool.jar d -d my.app.apk out
    

    В результате в директории out/smali у вас будет куча .java файлов с закомментированным Smali кодом внутри. Это нормально, так и должно быть.
  2. Добавьте атрибут android:debuggable="true" в секцию <application> файла out/AndroidManifest.xml
  3. Соберите директорию out обратно в .apk файл:
    java -jar apktool.jar b -d out my.app.to.debug.apk
    

  4. Подпишите файл my.app.to.debug.apk и установите его на реальное устройство или эмулятор, на котором вы собираетесь его отлаживать.
  5. Удалите директорию out/build (она может помешать создать проект на шаге 6 и 7).
  6. Запустите NetBeans, кликните «File» -> «New Project». Выберите «Java» -> «Java Project with Existing Sources». Кликните «Next».
  7. Укажите out в качестве «Project Folder». Кликните «Next».
  8. Добавьте директорию out/smali в список «Source Package Folder». Кликните «Next», а затем «Finish». В результате в проект будут добавлены те самые .java файлы с закомментированным Smali кодом внутри.
  9. Запустите my.app.to.debug.apk на реальном устройстве или эмуляторе (если вы используете реальное устройство, то убедитесь что оно подключено к вашему компьютеры с помощью USB кабеля и ваша система его «видит»).
  10. Запустите DDMS, найдите своё приложение в списке и кликните на него. Запомните информацию в последней колонке, это номер порта, обычно что-то вроде 86xx/8700.
  11. В Netbeans кликните «Debug» -> «Attach Debugger» -> выберите «JPDA» и введите в поле «Port» 8700 (или какой там номер порта у вас был на предыдущем шаге). Остальные поля оставте без изменений. Кликните «OK».
  12. Теперь вы можете отлаживать приложение: на панели Netbeans станет можно кликать на соответствующие кнопки
  13. Установите breakpoint на интересующую вас инструкцию (да-да, на одну из тех инструкция из закомментированного Smali кода внутри тех самых .java файлов о которых я уже дважды до этого упоминал). Помните, что вы не можете устанавливать breakpoints на строчки начинающиеся с ".", ":" или "#". Только на инструкции Smali кода!
  14. Сделайте что-нибудь в приложении, что бы ваша breakpoint сработала. После этого вы сможете делать пошаговую отладку, просматривать значения полей и переменных и т.д.

Вот и всё, если вкратце.

Подводные камни


Без подвоха тут конечно никак. Обычно всё идёт хорошо, строго по инструкции, аж до шага 13. А вот на шаге 13 люди часто ставят breakpoint в самом начале кода приложения: например в методе onCreate(...) в activity с которой начинается выполнение приложения. Оно вроде бы и логично – если не совсем понятно откуда начинать отлаживать приложение, лучше начинать с самого начала. Однако в большинстве случаев дело не идёт. Отлаживаемое приложение работает себе как ни в чем не бывало, а подлый breakpoint в onCreate(...) ни в какую не желает срабатывать.

Это происходит из-за того что мы подсоединяем отладчик к уже работающему приложению. Это значит код в начале приложения (например в том же методе onCreate в activity с которой начинается выполнение приложения) уже выполнился, и ставить на него breakpoint'ы как правило (хотя не всегда конечно) бесполезно. Более того, когда мы присоединяем отладчик к работающему приложению, оно не останавливается пока не сработает наш breakpoint или пока мы его сами не остановим – об этом моменте также стоит помнить.

В своей следующей статье я показываю трюк, который позволяет отлаживать Java приложения для Android без исходного кода с самого начала, т.е. именно с того самого первого метода onCreate(...) (или даже конструктора) в activity с которой начинается выполнение приложения.

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

Happy debugging!
+21
14528
145

Комментарии (22)

0
TheDimasig, #
Интересная статья, а можете привести реальный пример, где этот способ может пригодиться?
0
dimakovalenko, #
Спасибо за ваш отзыв.

Этот способ интересен в основном для reverse engineers — например для вирусных аналитиков или хакеров. Иногда этот способ полезен в ситуации «есть приложение, а исходники и документацию потеряли, разрабы уволились — теперь надо хотя бы примерно понять как оно там внутри работает». Хотя такое редко бывает конечно.
0
vovkab, #
В самом начале статьи:… reversing engineering приложений для Android
+1
fader44, #
Вот реальный пример использования: habrahabr.ru/post/127637/
0
TheDimasig, #
Осталось выбрать способ реализации. Так как я не имею не малейшего представления о разработке под андроид (да и вообще с Java не сильно знаком), да и писать свое приложение ради такой мелочи — это стрельба из пушки по воробьям, было решено модифицировать уже имеющийся будильник.


Вы уверены, что ваш способ не был «стрельбой из пушки по воробьям»)))
0
vilgeforce, #
Круто-круто! А не знаете ли, есть способ без плясок с бубном: просто аттачится к приложению без пересборки пакета? Я так понимаю, пересборка нужна только чтобы отладчик на PC имел хоть какой-то исходный код?
0
dimakovalenko, #
Спасибо за позитивный отзыв.

Увы, пока что простого способа не нашел. Насколько я знаю, нельзя вот так просто взять и скомандовать по JDWP приложению: «А ну стой! Какую инструкцию виртуальной машины Dalvik сейчас выполняешь? Какие значения переменных? Сколько солдат в твоем отряде потоков в приложении?»

Поэтому приходится извращаться. Пересборка с опцией -d нужна
(1) для того что бы добавить отладочную информацию в пакет, соответствие «инструкция Smali — строка в исходном коде», ну и получить сам исходный код
(2) а также выставить android:debuggable=«true».

Без (1) и (2) этого отладка к сожалению «не заводится».
0
vilgeforce, #
А как выглядит отладка? Минимальный шаг — одна инструкция Dalvik или 1 строка сорцов?
0
dimakovalenko, #
Отладчик выглядит странно. Мы имеем NetBeans что-то вроде

package ch.racic.android.marketenabler; class ExecReceiver {/*
а тут у нас инструкции Dalvik в формате ассемблера Smali
*/


Т.е. класс ExecReceiver фактически пустой с точки зрения языка Java, все инструкции Dalvik — внутри одного большого комментария. Но тем не менее NetBeans может выполнять эти инструкции внутри комментария одна за другой. Одна инструкция Dalvik за один шаг отладки. И после каждого шага можно просмотреть значение полей, локальных регистров и т.п.

Поначалу это смотрится немного странно, потом как-то привыкаешь.
0
vilgeforce, #
А, кажется понимаю. При ребилде производится генерация новой отладочной информации, чтобы она соответствовала сгенеренным сорцам. Просто была идея сделать фейковые сорцы, благо в dex часто есть отладочная информация. Но, похоже, не выйдет :-(
0
dimakovalenko, #
Так оно и есть. Apk-tools генерируют новую отладочную информацию, ну а android:debuggable=true мы сами уже добавляем, руками. Метод конечно… как гланды удалять через не предназначенное для это отверстие ниже спины. Но более толкового способа сделать отладку без сорцов пока что не нашел. Если что-то знаете по этому поводу — пишите в комментарии, добавлю в конец статьи как альтернативу изложенному с статье методу.
0
vilgeforce, #
Да я пока более «рабочего» чем у вас метода и сам не видел :-( А надо :-(
0
dimakovalenko, #
Ну у меня было пару идей, например:
(1) Прикрутить к Dalvik возможность по протоколу JDWP отзываться на вопросы вроде «Пауза! Какая инструкция выполнялась последней? Какое значение локально регистра v0?» Всё-таки исходники Dalvik открыты, можно поковыряться… Но времени нету, да и C++ программист из меня — как из говна пуля.
(2) Ещё была одна идея, которая заключалась в получении Smali кода (с помощью того же дизассемблера Smali), а потом патчинге этого самого кода так что бы его можно было потом отлаживать. Идея не очень изящная, такой себе мегакостыль…
0
Asen, #
Жаль, что на самом важном моменте вы так плохо все расписали( хотя в целом статья очень даже полезная ). Конкретно, пункт 11. «или какой там у вас порт» — у меня — 8600/8700, как, собственно, у всех. Так какой же порт указывать при присоединении дебаггера к Dalvik-процессу? Debugging происходит в Real-Time моде или пакет нужно каждый раз пересобирать?
0
dimakovalenko, #
Давненько меня тут не было, ну да все равно отвечу. Типа лучше поздно чем никогда.

По поводу «плохо расписал» — ну уж как смог так и расписал :)

По поводу порта — порт указываем тот который 86xx (вместо xx у вас каждый раз будут какие-то свои цифери естественно).

Про Real-Time вопроса не понял, извините.

Если есть вопросы по какой-то конкретной APK-шке — пишите в мыло, попробую помочь.
0
UZER2006, #
Возможно ли таким методом отлаживать системные jar-файлы, находящиеся в папке /system/framework?
0
Asen, #
Эти файлы, насколько знаю, лишь программный интерфейс к более низкому уровню программного стека Android. Испортив допустив ошибку в отладке и последующей успешной компиляции, все приложения( включая системные, конечно же ) полягут, что может привести к непредвиденному результату.
0
UZER2006, #
Непредвиденный результат – это максимум неработоспособная прошивка, или что-то более серьёзное типа потери загрузчика или recovery?
0
Asen, #
Одним словом, ничего хорошего. Все зависит от того, какого рода приложение( приложения ) используют тот или иной компонент интерфейса.
Все может закончиться, например, тем, что перестанет работать Bluetooth( если производилась не успешная отладка модуля, отвечающего за этот функционал ), или же, например, рухнет вся система, если была ошибка при перекомпиляции core.jar.

Да и вообще, jar и apk — это совершенно разные форматы. Даже не знаю, почему вы вдруг решили, что jar и apk разбираются по-одинаковому.
0
dimakovalenko, #
Черт, вы же спрашивали про JAR… Я чего-то завтыкал и подумал что речь идёт о DEX. Извините, насчет JAR ничего не могу сказать.
0
dimakovalenko, #
Теоретически — да, на практике — я бы не стал (см. комментарий от Asen).
0
UZER2006, #
Из опыта: JAR-файлы фреймворка отлаживаются нормально, только в дополнение нужно пересобирать framework-res, создавать проект на его папке, в исходниках добавлять smali нужных jar-файлов (на 8-м шаге), и вешать отладчик на system_process.
Также стоит уточнить, что после пересборки framework-res система будет очень плохо себя чувствовать – системные процессы падают один за другим, создаётся впечатление, что вот-вот всё рухнет – но всё же кое-как работает.

Только зарегистрированные пользователи могут оставлять комментарии.
Войдите, пожалуйста.