Android应用程序:java / JNI调用挂钩策略

时间:2015-11-02 13:23:44

标签: hook call android-source

我的目标是检测AOSP,以便动态记录来自目标应用程序的所有Java或JNI调用,包括或不包含参数和返回值。我不想修改应用程序,这就是为什么我要修改Android源代码。我对AOSP及其众多的库和框架没有很多经验,所以我正在寻找建议,因为我不知道从哪里开始。此外,由于记录了潜在的行数,因此该过程必须高效(即我不相信类似于调试的方法,其中必须为每个挂钩方法实现一个钩子类,可以工作)

到目前为止我所理解的是:

使用相对较新的ART系统,它将DEX应用程序源代码编译成一种机器可执行代码(OAT?),与Dalvik相比,它更加复杂。

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

如果我尝试挂钩根(Android API + libs.so),则需要花费大量工作来挂钩每个调用。理想的是所有java调用都通过的地方。 ART会出现这样的情况吗?

AOSP源代码很难理解,因为似乎没有文档说明每个源文件在全局架构中的作用。那么挂钩电话哪个更好?

修改(S)

这个主题没有很好地涵盖,所以我会向任何感兴趣的人显示信息。

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

这不是我正在寻求的,但我会尝试实施一个适合我需要的动态分析的AOSP补丁。

2 个答案:

答案 0 :(得分:10)

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

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

ArtMethod :: Invoke似乎用于反射和直接使用指向OAT代码部分的指针调用方法。 (同样,没有文档,它可能是不准确的。)

一般执行最终调用DoCall。

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

第一步是禁用这些优化:

在device / brand-name / model / device.mk中(在我的情况下,设备/ lge / hammerhead / device.mk用于nexus 5):

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

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

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

取消注释“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中禁用直接代码偏移量调用:

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

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

在art / runtime / interpreter / interpreter_common.h中,在include的开头添加:

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

在art / runtime / interpreter / interpreter_common.cc中,在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

为了定位应用程序,我使用了一个设置目标pid的属性。 为此,我们需要lib system / core / libcutils,这个lib仅在为真实手机编译AOSP时才可用(不会弄乱当前的makefile)。
因此,该解决方案不适用于模拟器。 (只猜测,从未尝试编辑:确认,“cutils / properties.h”无法添加到模拟器的构建中。)

编译并刷新修补的AOSP后,启动一个应用程序,ps | grep用于查找PID并在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会很快被填满。

PS:文件路径和名称与Android 5版本(Lollipop)匹配,它们可能与高级版本不同。

PS':如果有人想要打印方法的参数,我建议它查看art / runtime / utils.cc以获取PrettyArguments方法,并在代码中的某处找到一些实际的实现。

答案 1 :(得分:3)

也许你可以从Xposed项目中获得更多的想法,它通过禁用方法内联和直接分支优化来遵循相同的方法:

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

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