- 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
Нажмите здесь для печати.