- PVSM.RU - https://www.pvsm.ru -
В двух предыдущих статьях я рассказывал как отлаживать приложения для Android без исходного кода на Java [1] и о некоторых особенностях установки breakpoints [2]. Если уважаемый читатель ещё не ознакомился с этими статьями — я настоятельно рекомендую начать с них, а уже потом читать эту статью.
Так уж вышло что до сих пор я рассказывал исключительно об отладке байткода Dalvik и ни словом не обмолвился об отладке native методов. А ведь именно в native методах часто скрывается самое вкусное — хитрые защиты, интересные malware фичи, уязвимости нулевого дня. Поэтому сегодня я сжато, без «воды», расскажу как отлаживать native методы без исходного кода на C/C++ (ну или на чем, уважаемый читатель, они у вас там написаны).
Что бы извлечь пользу из моего рассказа нужно быть уже немного «в теме». В частности желательно что бы читатель
Вот пожалуй и все знания и навыки которые будут нужны читателю. Перейдём к инструментам.
Сегодня нам понадобится:
adb
из последней версии Android SDK [14]. Установка описана здесь [15].Перейдём к подготовке.
Предполагается что читатель достаточно опытен что бы самостоятельно выполнить следующие подготовительные действия:
папка/куда/дизассемблировали/приложение/smali
и выяснить:
папка/куда/дизассемблировали/приложение/lib
либо с устройства на котором будет производиться отладка, исследовать её и узнать как называются JNI функции которые соответствуют тем или иным native методам вызываемым из байткода Dalvik.-d
и загнать по отладку в NetBeans как написало в этой моей статье [1].System.loadLibrary(...)
или System.load(...)
, которые загружают .so с интересующими нас JNI функциями, но до первого вызова какого-либо из native методов. Можно использовать трюк о котором я писал тут [2].Теперь, когда все приготовления закончены, можно переходить собственно к отладке.
Идея в том, что бы загнать наше приложение сразу под два отладчика: под отладчик встроенный в NetBeans — что бы отлаживать байткод Dalvik, и под gdb — исключительно что бы отлаживать вызовы native методов. Звучит слегка странно, но на практике вполне себе работает. Хотя и не всегда — см. следующий раздел «Подводные камни».
Итак, если читатель выполнил все подготовительные действия из предыдущего раздела «Подготовка», то сейчас у него на устройстве или эмуляторе наверняка запущено пересобранное приложение, на компьютере открыт NetBeans и отладка стоит где-нибудь после вызова System.load(...)
или System.loadLibrary(...)
, но ещё до первого вызова native метода. Причем читатель уже в курсе в какой библиотеке какие JNI функции каким native методам соответствуют. С этого мы и начнём.
Дальше идёт пошаговая инструкция. Она писалась для Windows, но думаю будет работать и для Linux и MacOS. Пожалуйста, следуйте инструкции в точности:
abd shell
откройте ADB консоль вашего устройства или эмулятора. Найдите PID процесса вашего приложения воспользовавшись командой ps
в ADB консоли. В этой же консоли выполните команду:
gdbserver :5039 --attach %PID%
где %PID%
и есть PID процесса вашего приложения. В ответ gdbserver должен вывести что-то вроде:
Attached; pid = %PID%
Listening on port 5039
Начиная с этого момента отладка в NetBeans «замёрзнет». Т.е. вы конечно сможете там кликать на кнопки, но это бесполезно т.к. приложение которое вы пытаетесь отлаживать в NetBeans в данный момент остановлено под отладчиком GDB. Не паникуйте, всё так и должно быть!
adb forward tcp:5039 tcp:5039
на вашем компьютере.
gdb libMyNativeLibrary.so
где gdb libMyNativeLibrary.so
— та самая .so библиотека в которой находятся интересующие нас JNI функции. В результате откроется консоль gdb.
(gdb) target remote :5039
После этих манипуляций консоли gdb должно появится сообщение наподобии
Remote debugging using :5039
0x4009d58c in ?? ()
а в консоли ADB (она у нас ещё открыта, помните?) что-то типа
Remote debugging from host 127.0.0.1
(gdb) info functions
что бы увидеть список функций. В списке среди прочих функций должны быть и интересующие вас JNI функции, что-то типа:
0x5b5f7bac Java_my_app_for_debug_MainActivity_coolNativeMethod
0x5b5f7c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod
0x5b5f7c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
(gdb) break *0x5b5f7bac
(gdb) break *0x5b5f7c0c
(gdb) break *0x5b5f7c1c
c
в GDB консоли. После выполнения этой команды, отладка в NetBeans «размёрзнется» и вы снова сможете отлаживать байткод Dalvik.Теперь, каждый раз когда в байткоде Dalvik будет встречаться вызов native метода вроде
const/high16 v8, 0x4100
invoke-static {v8}, Lmy/app/for/debug/MainActivity;->theCoolestNativeMethodEver(F)V
отладка в NetBeans будет замирать, зато gdb будет вас радовать сообщениями вроде
Breakpoint 1, 0x5b5f7c1c in Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver () from libMyNativeLibrary.so
Вот это собственно оно и есть. Дальше x/i $pc
, stepi
— в общем вперёд отлаживать одну ARM-инструкцию за другой (помните я говорил в начале что ARM ассемблер будет нужен? — ну вот...)
О, их тут много. Целый сад подводных камней. Вот наиболее запоминающиеся глюки, которые попались мне при использовании GNU gdb (GDB) 7.4.1 в связке с GNU gdbserver (GDB) 7.4.1 на Android 4.0.3 в устройстве Ainol Aurora (той, старой ещё [16]):
set watchdog 18000
— это должно помочь.info functions
в списке функций отображаются не адреса функций в памяти, а смещения в .so файле, например:
0x000c0bac Java_my_app_for_debug_MainActivity_coolNativeMethod
0x000c0c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod
0x000c0c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
В этом случае положите libMyNativeLibrary.so
в каталог, который для gdb является текущим при старте, и перезапустите gdb снова то й же самой командной gdb libMyNativeLibrary.so
.
info functions
в списке функций отображаются и адреса функций в памяти и смещения в .so файле, например:
0x5b5f7bac Java_my_app_for_debug_MainActivity_coolNativeMethod
0x5b5f7c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod
0x5b5f7c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
0x000c0bac Java_my_app_for_debug_MainActivity_coolNativeMethod
0x000c0c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod
0x000c0c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
Игнорируйте смещения в .so файле, ставьте breakpoints на адреса функций в памяти.
break
на имена функций работает нормально — вы счастливчик, если нет… ну собственно у меня она и не работает, поэтому в данной статье я ставлю breakpoints на адреса функций.set stop-on-solib-events
. У меня не работает.Cannot access memory at address 0x1
. Игнорируйте.Я уверен что это далеко не полный список глюков, которые таит в себе отладка native методов без исходных кодов, и что другим исследователям попадутся совершенно другие, уникальные глюки которые не попались мне. Если кто что ещё найдёт — прошу делится в комментариях. Также в комментария прошу задавать вопросы и/или вносить технические поправки к тексту. Постараюсь ответить как можно быстрее, но если буду тупить — наберитесь пожалуйста терпения. Постараюсь ответить всем.
Happy debugging!
P.S. Просьба к минусующим статью отписываться в комментариях что именно не понравилось. Постараюсь исправить, если это возможно.
Автор: dimakovalenko
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/15715
Ссылки в тексте:
[1] как отлаживать приложения для Android без исходного кода на Java: http://habrahabr.ru/post/150825/
[2] о некоторых особенностях установки breakpoints: http://habrahabr.ru/post/150862/
[3] Smali: http://code.google.com/p/smali/
[4] Apktool: http://code.google.com/p/android-apktool/
[5] Java Native Interface (JNI): http://en.wikipedia.org/wiki/Java_Native_Interface
[6] как это работает: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
[7] System.load(...): http://developer.android.com/reference/java/lang/System.html#load(java.lang.String)
[8] System.loadLibrary(...): http://developer.android.com/reference/java/lang/System.html#loadLibrary(java.lang.String)
[9] начальном уровне: http://www.eng.auburn.edu/~nelson/courses/elec5260_6260/ARM_AssyLang.pdf
[10] ассемблер ARM: http://infocenter.arm.com/help/topic/com.arm.doc.dui0068b/index.html
[11] gdb и gdbserver: http://www.gnu.org/software/gdb/documentation/
[12] Android NDK: http://developer.android.com/tools/sdk/ndk/index.html
[13] тут: http://developer.android.com/tools/sdk/ndk/index.html#Installing
[14] Android SDK: http://developer.android.com/sdk/index.html
[15] здесь: http://developer.android.com/sdk/installing/index.html
[16] той, старой ещё: http://habrahabr.ru/post/138243/
Нажмите здесь для печати.