2015-11-02 11 views
5

Il mio obiettivo è quello di strumentare l'AOSP per registrare dinamicamente tutte le chiamate java o JNI da un'app mirata, con o senza gli argomenti e il valore restituito. Non voglio modificare l'applicazione, è per questo che sto cercando di modificare il codice sorgente Android. Non ho molta esperienza con AOSP e la sua moltitudine di librerie e librerie quindi sono alla ricerca di consigli perché non so da dove cominciare. Inoltre, a causa della quantità potenziale di linee registrate, il processo deve essere efficiente (cioè non credo che un metodo di debug-like, dove si debba implementare una classe hook per ogni metodo hooked, possa funzionare)Applicazione Android: java/JNI strategie di aggancio chiamate

Cosa Ho capito fino ad ora:

Con il relativamente nuovo sistema ART, esso compila il codice sorgente dell'app DEX in una sorta di codice eseguibile della macchina (OAT?) Ed è più complesso rispetto a quello che è stato con Dalvik.

Il flusso di esecuzione: compilato bytecode java dell'app (che dipende dall'API Android compilata) + libs.so -> DVM -> biforcuta VM di Zygote -> Esecuzione dell'applicazione.

Se provo ad agganciare alla radice (API Android + libs.so) richiederà una quantità di lavoro fastidiosa per agganciare ogni chiamata. L'ideale sarebbe un punto in cui passano tutte le chiamate java. Esiste forse un tale spot con ART?

Il codice sorgente AOSP è difficile da capire perché sembra che non ci sia un documento che stabilisce il ruolo di ciascun file sorgente nell'architettura globale. Quindi dove è meglio agganciare le chiamate?

EDIT (s)

Questo argomento non è ben coperto, quindi te lo mostrerò informazioni per chiunque sia interessato.

Le mie ricerche si sono imbattute in questo blog: http://blog.csdn.net/l173864930/article/details/45035521. progetto (+ Google translate) Chi si collega a questo interessante e Java ELF (braccio) chiamata aggancio: https://github.com/boyliang/AllHookInOne

Non è esattamente quello che sto cercando, ma cercherò di implementare una patch AOSP per l'analisi dinamica che si adatta i miei bisogni.

risposta

9

Sono riuscito a rispondere alla mia domanda. Per quello che ho potuto capire dal codice sorgente Ci sono 3 possibili punti di ingresso per le chiamate Java:

  • ArtMethod :: Invoke (arte/runtime/mirror/art_method.cc)
  • Execute (art/runtime /interpreter/interpreter.cc)
  • DoCall (arte/runtime/interprete/interpreter_common.cc)

ArtMethod :: Invoke sembra essere usato per la riflessione e per chiamare il metodo direttamente con un puntatore al OAT sezione di codice. (Ancora una volta, nessuna documentazione, può essere inesatta).

Execute end up chiama DoCall in generale.

Esistono alcune ottimizzazioni di ART che rendono difficile lo studio delle chiamate Java, come l'inlining dei metodi e il richiamo degli indirizzi di offset diretto.

il primo passo è la disabilitazione di queste ottimizzazioni:

Nel dispositivo/marca/modello/device.mk (nel mio caso dispositivo/LGE/martello/device.mk per un Nexus 5):

Aggiungere l'opzione "solo interpretazione" a dex2oat. Con questa opzione, ART compila solo il percorso di classe di avvio, quindi le applicazioni non verranno compilate in OAT.

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

Il secondo passo è disattivare espansione in arte/compilatore/dex/frontend.cc:

Uncomment "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; 

L'ultimo passo è disattivare compensare codice invocazione diretta nell'arte/compilatore/driver/compiler_driver.cc:

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

Con queste modifiche tutte diverse chiamate cadranno nella funzione DoCall dove possiamo infine aggiungere la nostra routine di registrazione mirata.

Nell'arte/runtime/interprete/interpreter_common.h, aggiungi all'inizio del include:

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

Nell'arte/runtime/interprete/interpreter_common.cc, aggiungere all'inizio della funzione 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 

Per il targeting dell'applicazione utilizzo una proprietà che imposta il pid di destinazione. Per questo abbiamo bisogno del sistema lib/core/libcutils e questa lib è disponibile solo quando l'AOSP è compilato per un telefono reale (senza scherzare con i makefile correnti).
Quindi la soluzione non funzionerà per un emulatore. ( Solo ipotesi, mai provato EDIT: confermato, "cutils/properties.h" non può essere aggiunto alla build di un emulatore).

Dopo aver compilato e visualizzato l'AOSP con patch, avviare un'app, ps | grep per trovare il PID e impostare la proprietà di 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>() 
[...] 

quando è finita:

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

Voilà!

Il sovraccarico non è visibile da un punto di vista dell'utente, ma il logcat verrà riempito rapidamente.

PS: percorsi di file e nomi corrispondono alla versione Android 5 (Lollipop), saranno probabilmente diversi con versioni superiori.

PS ': Se uno vorrebbe stampare gli argomenti dei metodi, consiglierei di guardare art/runtime/utils.cc per il metodo PrettyArguments e di trovare qualche implementazione pratica da qualche parte nel codice.

+0

Grazie tanto per spiegare questo. Ho una domanda. Se voglio applicare il patch al codice AOSP come hai spiegato e creare un'immagine del sistema Android per un emulatore, dovrei aggiungere l'opzione "solo-interpretabile"? Non sono riuscito a trovare il file device.mk per l'emulatore. Gentilmente fammi sapere. – aMa

+0

Qual è il tuo dispositivo di destinazione? Il mio era un nexus 5 hammerhead quindi il mio percorso è /device/lge/hammerhead/device.mk. –

+0

Sry, ho letto troppo velocemente. Non ho mai provato un emulatore e probabilmente avrai problemi con libcutils per la funzione property_get. Hai già provato senza le modifiche di device.mk? Dovrebbe funzionare senza di esso nel peggiore dei casi. Senza di esso la cosa peggiore che può accadere è che ti manca alcune delle chiamate. –

2

Forse è possibile ottenere ulteriori idee di progetto Xposed, che seguono lo stesso approccio disabilitando il metodo inline e l'ottimizzazione succursali dirette:

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

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

+0

Thx per il vostro interesse. Conosco già il progetto Xposed. In realtà, dopo aver trovato come disabilitare la funzione di inlining, ho trovato il progetto github e ho controllato ogni commit git. È così che ho scoperto di disabilitare quello che chiamate diramazione diretta, tutti i crediti per questo vanno a favore degli autori di Xposed. –