2015-11-02 50 views
5

Moim celem jest instrument AOSP w celu dynamicznego rejestrowania wszystkich wywołań Javy lub JNI z wybranej aplikacji, z lub bez argumentów i wartości zwracanej. Nie chcę modyfikować aplikacji, dlatego szukam modyfikacji kodu źródłowego Androida. Nie mam dużego doświadczenia z AOSP i jego mnóstwem bibliotek i frameworków, więc szukam porad, ponieważ nie wiem, od czego zacząć. Co więcej, z powodu potencjalnej ilości rejestrowanych linii, proces musi być sprawny (tj. Nie wierzę, że metoda debugowania, w której trzeba zaimplementować klasę haczyków dla każdej zahaczonej metody, może działać)Android: java/JNI call hooking strategy

Co Rozumiem do tej pory:

Ze stosunkowo nowym systemem ART, kompiluje kod źródłowy aplikacji DEX do kodu wykonywalnego maszyny (OAT?) I jest bardziej skomplikowany w porównaniu do tego, co było z Dalvik.

Przepływ wykonania: skompilowany kod bajtowy Java aplikacji (który zależy od skompilowanego interfejsu API Androida) + libs.so -> DVM -> rozwidlona maszyna wirtualna Zygote -> Wykonanie aplikacji.

Jeśli spróbuję podłączyć się do root'a (Android API + libs.so), będzie wymagać dużej ilości pracy, aby połączyć każde połączenie. Ideałem byłoby miejsce, w którym przechodzą wszystkie połączenia java. Czy takie miejsce istnieje nawet z ART?

Kod źródłowy AOSP jest trudny do zrozumienia, ponieważ wydaje się, że nie ma dokumentu określającego rolę każdego pliku źródłowego w architekturze globalnej. Więc gdzie lepiej jest przechwycić połączenia?

EDIT (s)

Ten temat nie jest dobrze osłonięte, więc pokażę informacji dla wszystkich zainteresowanych.

Moje badania natknęły się na tym blogu: http://blog.csdn.net/l173864930/article/details/45035521. (+ Google translate) Kto Linki do tego ciekawą Java i ELF (ARM) Zaproszenie zaczepiając projekt: https://github.com/boyliang/AllHookInOne

To nie jest dokładnie to, czego szukam, ale postaram się wdrożyć AOSP patch do analizy dynamicznej, który odpowiada moje potrzeby.

Odpowiedz

9

Udało mi się odpowiedzieć na moje pytanie. Na co mogę zrozumieć z kodu źródłowego nie jest możliwe 3 punkty wejścia dla połączeń Java:

  • ArtMethod :: Invoke (sztuka/czas pracy/lustro/art_method.cc)
  • Execute (sztuka/czas pracy /interpreter/interpreter.cc)
  • DoCall (sztuka/czas pracy/tłumacz/interpreter_common.cc)

ArtMethod :: Invoke wydaje się być wykorzystywane do refleksji i wywołanie metody bezpośrednio ze wskaźnikiem do OAT sekcja kodu. (Ponownie, brak dokumentacji, może być niedokładna).

Wykonanie w końcu wywołanie DoCall ogólnie.

Istnieje kilka optymalizacji ART, które utrudniają naukę wywołań Java, takich jak metoda inline i bezpośrednie wywoływanie adresów offsetowych.

Pierwszym krokiem jest wyłączenie tych optymalizacje:

W urządzeniu/markowy/model/device.mk (w moim przypadku urządzenia/LGE/Hammerhead/device.mk dla Nexus 5):

Dodaj opcję "tylko do interpretacji", aby dex2oat. Dzięki tej opcji ART kompiluje tylko ścieżkę klasy rozruchowej, więc aplikacje nie będą kompilowane w OAT.

PRODUCT_PROPERTY_OVERRIDES := \ 
    dalvik.vm.dex2oat-filter=interpret-only 

Drugi etap polega na wyłączeniu inline w sztuce/kompilatora/dex/frontend.cc:

Odkomentuj "kSuppressMethodInlining".

/* Default optimizer/debug setting for the compiler. */ 
static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations 
    (1 << kLoadStoreElimination) | 
    // (1 << kLoadHoisting) | 
    // (1 << kSuppressLoads) | 
    // (1 << kNullCheckElimination) | 
    // (1 << kClassInitCheckElimination) | 
    (1 << kGlobalValueNumbering) | 
    // (1 << kPromoteRegs) | 
    // (1 << kTrackLiveTemps) | 
    // (1 << kSafeOptimizations) | 
    // (1 << kBBOpt) | 
    // (1 << kMatch) | 
    // (1 << kPromoteCompilerTemps) | 
    // (1 << kSuppressExceptionEdges) | 
    (1 << kSuppressMethodInlining) | 
    0; 

Ostatnim krokiem jest wyłączenie kodu offsetu bezpośredniego wywołania w sztuce/kompilator/kierowcy/compiler_driver.cc:

-bool use_dex_cache = GetCompilerOptions().GetCompilePic(); 
+bool use_dex_cache = true; 

z tymi zmianami wszystkim różne połączenia spadnie w funkcji DoCall gdzie możemy wreszcie dodaj naszą ukierunkowaną procedurę logowania.

w art/runtime/tłumacz/interpreter_common.h, dodać na początku obejmuje:

#ifdef HAVE_ANDROID_OS 
#include "cutils/properties.h" 
#endif 

w art/runtime/tłumacz/interpreter_common.cc, dodać na początku funkcji DoCall:

#ifdef HAVE_ANDROID_OS 
    char targetAppVar[92]; 
    property_get("target.app.pid", targetAppVar, "0"); 

    int targetAppPID = atoi(targetAppVar); 

    if(targetAppPID != 0 && targetAppPID == getpid()) 
    LOG(INFO) << "DoCall - " << PrettyMethod(method, true); 
#endif 

Do kierowania aplikacji używam właściwości, która ustawia docelowy pid. Do tego potrzebujemy systemu lib/core/libcutils, a ta lib jest dostępna tylko wtedy, gdy AOSP jest skompilowany dla prawdziwego telefonu (bez zmywania z obecnymi plikami Makefile).
Rozwiązanie nie będzie działać na emulatorze. ( Tylko zgadywanie, nigdy nie próbowano EDYCJA: potwierdzona, "cutils/properties.h" nie mogą być dodane do kompilacji emulatora).

Po skompilowaniu i miganiu załatanego AOSP, uruchom aplikację, ps | grep do znalezienia PID i ustawić właściwość root:

[email protected]:/ # ps | grep contacts          
u0_a2  4278 129 1234668 47356 ffffffff 401e8318 S com.android.contacts 

[email protected]:/ # setprop target.app.pid 4278 

[email protected]:/ # logcat 
[...] 
I/art  (4278): DoCall - int android.view.View.getId() 
I/art  (4278): DoCall - void com.android.contacts.activities.PeopleActivity$ContactsUnavailableFragmentListener.onCreateNewContactAction() 
I/art  (4278): DoCall - void android.content.Intent.<init>(java.lang.String, android.net.Uri) 
I/art  (4278): DoCall - void android.app.Activity.startActivity(android.content.Intent) 
I/ActivityManager( 498): START u0 {act=android.intent.action.INSERT dat=content://com.android.contacts/contacts cmp=com.android.contacts/.activities.ContactEditorActivity} from uid 10002 on display 0 
V/WindowManager( 498): addAppToken: AppWindowToken{3a82282b token=Token{dc3f87a ActivityRecord{c0aaca5 u0 com.android.contacts/.activities.ContactEditorActivity t4}}} to stack=1 task=4 at 1 
I/art  (4278): DoCall - void android.app.Fragment.onPause() 
I/art  (4278): DoCall - void com.android.contacts.common.list.ContactEntryListFragment.removePendingDirectorySearchRequests() 
I/art  (4278): DoCall - void android.os.Handler.removeMessages(int) 
I/art  (4278): DoCall - void com.android.contacts.list.ProviderStatusWatcher.stop() 
I/art  (4278): DoCall - boolean com.android.contacts.list.ProviderStatusWatcher.isStarted() 
I/art  (4278): DoCall - void android.os.Handler.removeCallbacks(java.lang.Runnable) 
I/art  (4278): DoCall - android.content.ContentResolver com.android.contacts.ContactsActivity.getContentResolver() 
I/art  (4278): DoCall - void android.content.ContentResolver.unregisterContentObserver(android.database.ContentObserver) 
I/art  (4278): DoCall - void android.app.Activity.onPause() 
I/art  (4278): DoCall - void android.view.ViewGroup.drawableStateChanged() 
I/art  (4278): DoCall - void com.android.contacts.ContactsActivity.<init>() 
I/art  (4278): DoCall - void com.android.contacts.common.activity.TransactionSafeActivity.<init>() 
I/art  (4278): DoCall - void android.app.Activity.<init>() 
I/art  (4278): DoCall - void com.android.contacts.util.DialogManager.<init>(android.app.Activity) 
I/art  (4278): DoCall - void java.lang.Object.<init>() 
[...] 

Kiedy to się skończy:

[email protected]:/ # setprop target.app.pid 0

voila!

Przeciążenie nie jest zauważalne z punktu widzenia użytkownika, ale logcat zostanie szybko wypełniony.

PS: Ścieżki plików i nazwy pasują do wersji Androida 5 (Lollipop), będą prawdopodobnie inne w wersjach lepszych.

PS ': Jeśli ktoś chciałby wydrukować argumenty metod, radziłbym mu przyjrzeć się sztuce/runtime/utils.cc dla metody PrettyArguments i znaleźć jakąś praktyczną implementację gdzieś w kodzie.

+0

Dziękuję bardzo za wyjaśnienie tego. Mam pytanie. Jeśli chcę załatać kod AOSP, jak wyjaśniłeś i zbudować obraz systemu Android dla emulatora, gdzie powinienem dodać opcję "tylko do tłumaczenia"? Nie mogłem znaleźć pliku device.mk dla emulatora. Uprzejmie proszę dać mi znać. – aMa

+0

Jakie jest Twoje urządzenie docelowe? Mój był węzłem 5, więc moja ścieżka to /device/lge/hammerhead/device.mk. –

+0

Sry, właśnie czytałem zbyt szybko. Nigdy nie próbowałem emulatora i prawdopodobnie napotkasz problemy z libcutils dla funkcji property_get. Czy próbowałeś już bez modyfikacji pliku device.mk? Powinien działać bez niego w najgorszym przypadku. Bez tego najgorszą rzeczą, jaka może się stać, jest to, że przegapisz niektóre z połączeń. –

2

Może masz więcej pomysłów od projektu Xposed, to wykonaj to samo podejście, wyłączając metoda inline i bezpośrednia optymalizacja rozgałęzienia:

https://github.com/rovo89/android_art/commit/0f807a65612f77a46120a53d3caa12c2

https://github.com/rovo89/android_art/commit/92e8c8e0309c4a584f4279c478d54d8ce036ee59

+0

Thx za zainteresowanie. Znam już projekt Xposed. Właściwie, po tym jak odkryłem, jak wyłączyć inlining, znalazłem projekt Github i sprawdziłem każde zatwierdzenie git. Tak jak odkryłem, aby wyłączyć to, co nazywacie bezpośrednim rozgałęzianiem, wszystkie kredyty dla tego dotyczą autora (ów) Xposed. –