Android应用程式:Java/JNI呼叫挂接策略 [英] Android app : java / JNI call hooking strategies

查看:53
本文介绍了Android应用程式:Java/JNI呼叫挂接策略的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是检测AOSP,以便动态记录来自目标应用程序的所有Java或JNI调用,无论是否包含参数和返回值.我不想修改应用程序,这就是为什么我要修改Android源代码.我对AOSP及其大量的库和框架不是很有经验,所以我正在寻找建议,因为我不知道从哪里开始.而且,由于记录的行数可能很大,因此该过程必须高效(即,我认为像调试这样的方法(必须为每个钩子方法实现一个钩子类)无法正常工作)

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 :

使用相对较新的ART系统,它将DEX应用程序源代码编译为某种机器可执行代码(OAT?),并且与Dalvik相比,其处理起来更加复杂.

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.

执行流程:应用程序的已编译Java字节码(取决于编译的Android API)+ libs.so-> DVM->分叉的Zygote VM->应用程序的执行.

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.

如果我尝试挂接到根目录(Android API + libs.so),则挂接每个调用将需要大量的工作.理想的情况是所有Java调用都能通过的地方. ART甚至存在这样的位置吗?

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 ?.

很难理解AOSP源代码,因为似乎没有文档说明每个源文件在全局体系结构中的作用.那么挂断电话最好在哪里?

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 ?

编辑(s)

这个主题没有很好地介绍,所以我将向有兴趣的人显示信息.

This topic is not well covered, so I'll show info for anyone interested.

我的研究发现了这个博客: http://blog.csdn.net/l173864930/article/details/45035521 . (+ Google翻译) 谁链接到这个有趣的Java和ELF(手臂)呼叫挂钩项目: https://github.com/boyliang/AllHookInOne

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

这不是我要寻找的,但是我将尝试实现适合我需要的AOSP补丁进行动态分析.

It is not exactly what I'm seeking, but I will try to implement an AOSP patch for dynamic analysis that suits my needs.

推荐答案

我已经成功回答了我的问题. 从源代码中我可以理解,java调用有3个可能的入口点:

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(art/runtime/mirror/art_method.cc)
  • 执行(art/runtime/interpreter/interpreter.cc)
  • DoCall(艺术/运行时/解释器/interpreter_common.cc)

ArtMethod :: Invoke似乎用于反射和直接使用指向OAT代码部分的指针来调用该方法. (再次,没有文档,这可能是不准确的.)

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).

执行通常会最终调用DoCall.

Execute end up calling DoCall generally.

ART的一些优化使对Java调用的研究变得困难,例如方法内联和直接偏移地址调用.

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 :

在device/brand-name/model/device.mk中(在我的情况下为device/lge/hammerhead/device.mk,关系为5):

In device/brand-name/model/device.mk (in my case device/lge/hammerhead/device.mk for a nexus 5) :

将选项仅解释"添加到dex2oat.使用此选项,ART仅编译引导类路径,因此不会在OAT中编译应用程序.

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

第二步是在art/compiler/dex/frontend.cc中禁用内联:

The second step is to disable inlining in art/compiler/dex/frontend.cc :

取消注释"kSuppressMethodInlining".

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;

最后一步是在art/compiler/driver/compiler_driver.cc中禁用直接代码偏移量调用:

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;

通过这些更改,所有不同的调用都将落入DoCall函数中,在此我们最终可以添加目标日志记录例程.

With these changes all different calls will fall in the DoCall function where we can finally add our targeted logging routine.

在art/runtime/interpreter/interpreter_common.h中,在包含内容的开头添加:

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

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

在art/runtime/interpreter/interpreter_common.cc中,在DoCall函数的开头添加:

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

为目标应用程序,我使用一个设置目标pid的属性. 为此,我们需要使用lib system/core/libcutils,并且只有在为真实手机编译AOSP时才可用此lib(不与当前的makefile混淆).
因此,该解决方案不适用于仿真器. (仅猜测,从未尝试过已确认,"cutils/properties.h"无法添加到模拟器的内部.)

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 confirmed, "cutils/properties.h" cannot be added to the build of an emulator).

编译并刷新修补的AOSP之后,启动一个应用程序,ps |. grep查找PID并在root中设置属性:

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>()
[...]

结束后:

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

Voilà!

从用户的角度来看,过载并不明显,但是logcat将被快速填充.

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

PS:文件路径和名称与Android 5版本(Lollipop)匹配,而高级版本可能会有所不同.

PS : File paths and names match the Android 5 version (Lollipop), they will be probably different with superior versions.

PS':如果要打印方法的参数,我建议它查看一下PrettyArguments方法的art/runtime/utils.cc并在代码中的某个地方找到一些实际的实现.

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.

这篇关于Android应用程式:Java/JNI呼叫挂接策略的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆