Android app: java / JNI connection strategies

My goal is to use AOSP to dynamically register all Java or JNI calls from the target application with or without arguments and return value. I do not want to change the application, so I want to change the source code of Android. I am not very good at AOSP and its many libraries and environments, so I’m looking for advice because I don’t know where to start. In addition, due to the potential number of lines recorded in the log, the process should be efficient (I do not think that the debugging method, where you need to implement the hook class for each method with a binding, may work)

What I have understood so far:

With the relatively new ART system, it compiles the source code of the DEX application into a kind of machine executable code (OAT?), And it is more complex than what it was with Dalvik.

Execution thread: compiled java application byte code (which depends on the compiled Android API) + libs.so → DVM → Zygote VM plug → Application execution.

If I try to connect to the root directory (Android API + libs.so), it will require a lot of effort to connect each call. An ideal is where all java calls go. Is there such a place even with ART?

AOSP source code is hard to understand because there seems to be no document that states the role of each source file in the global architecture. So where is the best way to intercept calls?

EDIT (s)

This topic is not well covered, so I will show the information to everyone who is interested.

My research came across this blog: http://blog.csdn.net/l173864930/article/details/45035521 . (+ Google translate) Who references this interesting Java and ELF call binding project (arm): https://github.com/boyliang/AllHookInOne

This is not quite what I'm looking for, but I will try to implement an AOSP patch for dynamic analysis that fits my needs.

+5
source share
2 answers

I managed to answer my question. For what I could understand from the source code, there are 3 possible entry points for java calls:

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

ArtMethod :: Invoke, apparently, is used to reflect and to directly call a method with a pointer to a section of the OAT code. (Again, there is no documentation, it may be inaccurate).

Execution ultimately calls DoCall.

There are several ART optimizations that make it difficult to learn Java calls, such as the inline method and direct address call.

The first step is to disable these optimizations:

In the device / company name /model/device.mk (in my case, the device /lge/hammerhead/device.mk for communication 5):

Add the interpretation-only option to dex2oat. With this option, ART only compiles the download path, so applications will not be compiled into OAT.

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

The second step is to disable the attachment in art / compiler / 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; 

The last step is to disable the direct code offset call in the art / compiler / driver / compiler_driver.cc file:

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

With these changes, all the various calls will fall into the DoCall function, where we can finally add our target logging procedure.

In art / runtime / interpreter / interpreter_common.h, add include at the beginning:

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

In art / runtime / interpreter / interpreter_common.cc add the DoCall function at the beginning:

 #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 

To target the application, I use a property that sets the target pid. To do this, we need lib system / core / libcutils, and this library is only available if AOSP is compiled for a real phone (without interacting with the current make files).
Thus, the solution will not work for the emulator. ( Just guess, I never tried EDIT: confirmed, "cutils / properties.h" cannot be added to the emulator assembly).

After compiling and blinking the fixed AOSP, start the ps | grep to find the PID and set the property in the root:

 shell@android :/ # ps | grep contacts u0_a2 4278 129 1234668 47356 ffffffff 401e8318 S com.android.contacts shell@android :/ # setprop target.app.pid 4278 shell@android :/ # 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>() [...] 

When will it end:

shell@android :/ # setprop target.app.pid 0

Voilà!

Overload is not noticeable from the point of view of the user, but logcat will be quickly filled.

PS: The paths and file names correspond to the version of Android 5 (Lollipop), they are likely to differ from the best versions.

PS ': If you want to print the arguments of the methods, I would advise him to look at art / runtime / utils.cc for the PrettyArguments method and find some practical implementation somewhere in the code.

+9
source

Perhaps you can get more ideas from the Xposed project, they follow the same approach by disabling the inline method and direct branch optimization:

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

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

+2
source

Source: https://habr.com/ru/post/1235016/


All Articles