- PVSM.RU - https://www.pvsm.ru -
Это продолжения моей вчерашней статьи об отладке приложений для Adnroid без исходного кода на Java [1] (если кто-то её не читал — я очень советую начать с неё). Вчера я давал пошаговую инструкцию как настроить и начать использовать связку Apk-tool плюс NetBeans. Два последних пункта там звучали примерно так:
13. Установите breakpoint на интересующую вас инструкцию… blah-blah-blah...
14. Сделайте что-нибудь в приложении, что бы ваша breakpoint сработала. После этого вы сможете делать пошаговую отладку, просматривать значения полей и переменных и т.д.
Дальше, в разделе «Подводные камни», я рассказывал почему мы не может начать отладку приложения с самого начала, например поставив breakpoint на какую-нибудь инструкцию метода onCreate(...)
в activity, с которой начинает выполняться приложение.
В этой статье я расскажу как всё же можно начать отлаживать приложение без исходного кода на Java с самого начала. Эта статья опять-таки не для новичков. Нужно как минимум понимать синтаксис ассемблера Smali [2] и уметь ручками патчить .smali файлы, грамотно вписывая туда свой код.
Нам снова нужны Apk-tool 1.4.1 [3] и NetBeans 6.8 [4] — причем именно эти устаревшие на сегодняшний день версий. С более новыми версиями заставить работать отладку у меня не получается. И судя по дискуссиям на тематических форумах — не только у меня.
Установку Apk-tool и NetBeans я уже описывал во вчерашней статье, но всё же повторюсь. NetBeans устанавливается по умолчанию, просто кликаем Next-Next-Next в мастере установки. Установка Apk-tools заключается в обычном извлечении файла apktool.jar
из архива в любую папку.
Идея в общем-то простая. Нужно найти activity, которая стартует в приложении первой, и вписать бесконечный цикл в начало метода onCreate(...)
этой activity. Приложение стартует и сразу после вызова конструктора этой activity будет вызван метод onCreate(...)
. В результате управление попадёт в наш бесконечный цикл. Пока цикл будет там крутиться, мы неспеша присоединим отладчик к работающему приложению, поставим breakpoint сразу после нашего бесконечного цикла, а потом воспользуемся возможностями отладчика и сделаем так что бы управление из этого цикла вышло. И сразу же попало на наш breakpoint. Как видим, всё элементарно.
В этом разделе дана пошаговая инструкция. Инструкция написана для Windows, но скорее всего будет работать на Linux и Mac OS.
Пожалуйста в точности следуйте инструкции — это важно!
temp
с помощью Apk-tool. Не используйте опцию -d
:
java -jar apktool.jar d my.app.apk temp
В результате в директории temp/smali
у вас будет куча .smali файлов.
temp/AndroidManifest.xml
найдите activity с фильтрами
<intent -filter="-filter">
<action android:name="android.intent.action.MAIN">
<category android:name="android.intent.category.LAUNCHER">
</intent>
Это и есть activity которая стартует в приложении первой.
android.app.Activity
). Ищите в глубине директории temp/smali
.onCreate(...)
и сразу после вызова (обычно этот вызов идёт в самом начале)
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
в onCreate(...)
впишите следующий код:
:debug
sget v0, Lmy/activity/class/MyActivity;->debugFlag:I
if-nez v0, :debug
Внимательный читатель вероятно уже догадался, что этот код и есть тот бесконечный цикл о котором мы говорили раньше. Естественно, вместо v0
в этом коде можно использовать любой подходящий локальный регистр. Если подходящего регистра нет — добавьте его, отредактировав директивы .locals
и/или .registers
соответствующим образом.
.field static debugFlag:I = 0x01
иначе код бесконечного цикла в предыдущем пункте не заработает.
temp
обратно в ваш .apk файл, опять-таки без опции -d
:
java -jar apktool.jar b temp my.app.apk
Конечно, оригинальный my.app.apk
стоит где-нибудь перед этим сохранить.
Теперь у нас есть пропатченый my.app.apk
. В начало метода onCreate(...)
в классе той activity, с которой начинается выполнение приложения, мы вписали бесконечный цикл. Что ж, берите этот пропатченый my.app.apk
и следуйте пошаговой инструкции из моей вчерашней статьи [1] (см. раздел «Отладка»). Учтите, что на девятом шаге этой инструкции, после того как вы запустите приложение, вы увидите черный экран. Это нормально, так и должно быть! Это просто означает, что сразу после запуска приложения был вызван наш пропатченый метод onCreate(...)
и управление попало в наш бесконечный цикл. Если через некоторое время Android предложит вам закрыть приложение потому что оно не отвечает — откажитесь и идите дальше строго по инструкции!
На двенадцатом шаге инструкции откройте в NetBeans тот .java файл, в котором находится пропатченный вами метод onCreate(...)
. Воспользуйтесь кнопкой «пауза» на панели отладки в NetBeans. Затем в этом открытом .java файле поставьте breakpoint на первую инструкцию после кода бесконечного цикла, который вы вписали в onCreate(...)
. Потом, пользуясь функцией просмотра и редактирования переменных в отладчике NetBeans, поменяйте значение поля debugFlag
на 0
и кликните на кнопку «продолжить отладку» на панели отладки в NetBeans. Управление выйдет из бесконечного цикла и тут же попадёт на ваш breakpoint.
Всё, теперь можно спокойно отлаживать приложение фактически с самого начала самого первого onCreate(...)
!
Читатель, который немного в теме, наверное читал на тематических форумах про использование метода android.os.Debug.waitForDebugger()
для тех же целей, для которых мы в этой статье используем бесконечный цикл. И этот самый читатель вероятно удивлён, что мы тут нагородили какой-то огород с циклом, хотя можно было бы просто вписать в начало нашего onCreate(...)
вызов всего одного статического метода:
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
Заметьте, метод вызывается без параметров, а значит не нужно мучиться с добавлением локальных регистров если нету подходящего. Казалось бы — красота! Чего ещё надо?
В теории — ничего не надо, бери да пользуйся. Но на практике всё чуток сложнее. По факту фокус с android.os.Debug.waitForDebugger()
работает далеко не всегда и не у всех. У многих (в том числе и у меня) сразу после вызова android.os.Debug.waitForDebugger()
приложение действительно «замирает» и ждёт когда к нему присоединится отладчик. Это видно даже в DDMS — напротив приложения появляется маленький значок «красный жук». Но как только мы присоединяем отладчик к приложению, управление тут же переходит на следующую инструкцию после android.os.Debug.waitForDebugger()
и приложение начинает выполняться дальше без остановки. Мы просто не успеваем поставить breakpoint после android.os.Debug.waitForDebugger()
. Обсуждение по этому поводу см. например тут [5].
Почему android.os.Debug.waitForDebugger()
у кого-то работает так, а у кого-то эдак — мне пока что неизвестно. Может в комментариях кто-то даст пояснения по этому поводу. Также в комментариях можно и нужно задавать вопросы по статье. Постараюсь ответить по-возможности оперативно, но если буду тупить — пожалуйста наберитесь терпения. Постараюсь ответить всем.
Автор: dimakovalenko
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/14471
Ссылки в тексте:
[1] вчерашней статьи об отладке приложений для Adnroid без исходного кода на Java: http://habrahabr.ru/post/150825/
[2] Smali: http://code.google.com/p/smali/
[3] Apk-tool 1.4.1: http://code.google.com/p/android-apktool/downloads/detail?name=apktool1.4.1.tar.bz2&can=1&q=
[4] NetBeans 6.8: http://netbeans.org/downloads/6.8/index.html
[5] тут: http://stackoverflow.com/questions/3107587/android-with-jdb-confusion-using-waitfordebugger
Нажмите здесь для печати.