My goal is to instrument the AOSP in order to dynamically log all java or JNI calls from a targeted app, with or without the arguments and return value. I do not want to modify the application, it is why I am looking to modify the Android source code. I am not very experience with AOSP and its multitude of libs and frameworks so I am looking for advices because I don't know where to start. Moreover, because of the potential amount of lines logged, the process have to be efficient (i.e I do not believe that a debug-like method, where one must implements a hook class for each hooked method, can work)
What I understood so far :
With the relatively new ART system, it compiles the DEX app source code into a sort of machine executable code (OAT ?) and it is more complex to instrument compared to what it has been with Dalvik.
The execution flow : compiled java bytecode of the app (which depends of the compiled Android API) + libs.so -> DVM -> forked Zygote VM -> Execution of the app.
If I try to hook at the root (Android API + libs.so) it will demands a fastidious amount of work to hook each call. The ideal would be a spot where all java calls pass through. Does a such spot even exists with ART ?.
The AOSP source code is hard to understand because it seems that there are no document that states the role of each source file in the global architecture. So where it is better to hook the calls ?
EDIT(s)
This topic is not well covered, so I'll show info for anyone interested.
My researches came across this blog : http://blog.csdn.net/l173864930/article/details/45035521. (+Google translate) Who links to this interesting Java and ELF (arm) call hooking project : https://github.com/boyliang/AllHookInOne
It is not exactly what I'm seeking, but I will try to implement an AOSP patch for dynamic analysis that suits my needs.
I have succeed to answer my question. For what I could understand from the source code there is 3 possible entry points for the java calls :
ArtMethod::Invoke seems to be used for reflection and for calling the method directly with a pointer to the OAT code section. (Again, no documentation, it can be inexact).
Execute end up calling DoCall generally.
There is some optimizations of ART that make the study of Java calls difficult, like method inlining and direct offset address calling.
The first step is the disabling of these optimizations :
In device/brand-name/model/device.mk (in my case device/lge/hammerhead/device.mk for a nexus 5) :
Add the option "interpret-only" to dex2oat. With this option, ART compile only the boot classpath, so the applications will not be compiled in OAT.
PRODUCT_PROPERTY_OVERRIDES := \
dalvik.vm.dex2oat-filter=interpret-only
The second step is to disable inlining 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 direct code offset invocation in art/compiler/driver/compiler_driver.cc :
-bool use_dex_cache = GetCompilerOptions().GetCompilePic();
+bool use_dex_cache = true;
With these changes all different calls will fall in the DoCall function where we can finally add our targeted logging routine.
In art/runtime/interpreter/interpreter_common.h, add at the beginning of the includes :
#ifdef HAVE_ANDROID_OS
#include "cutils/properties.h"
#endif
In art/runtime/interpreter/interpreter_common.cc, add at the beginning of the DoCall function :
#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
For targeting the application I use a property which set the targeted pid.
For this we need the lib system/core/libcutils and this lib is only available when the AOSP is compiled for a real phone (without messing with the current makefiles).
So the solution will not work for an emulator. (Only guessing, never tried EDIT: confirmed, "cutils/properties.h" cannot be added to the build of an emulator).
After compiling and flashing the patched AOSP, start an app, ps | grep for finding the PID and set the property in 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 it is over :
shell@android:/ # setprop target.app.pid 0
Voilà !
The overload is not noticeable from a user point of view, but the logcat will be quickly filled.
PS : File paths and names match the Android 5 version (Lollipop), they will be probably different with superior versions.
PS' : If one would wants to print the arguments of the methods, I would advice it to look at art/runtime/utils.cc for the PrettyArguments method and to find some practical implementation somewhere in the code.
Maybe you can get more ideas from Xposed project, it follow the same approach by disabling the method inlining and direct branching optimization:
https://github.com/rovo89/android_art/commit/0f807a6561201230962f77a46120a53d3caa12c2
https://github.com/rovo89/android_art/commit/92e8c8e0309c4a584f4279c478d54d8ce036ee59
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With