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

Dr.*_*yll 5 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补丁.

Dr.*_*yll 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调用的研究变得困难,例如方法内联和直接偏移地址调用.

第一步是禁用这些优化:

在设备/品牌名称/型号/设备.mk(在我的情况下设备/ lge/hammerhead/device.mk为nexus 5):

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

PRODUCT_PROPERTY_OVERRIDES := \
    dalvik.vm.dex2oat-filter=interpret-only
Run Code Online (Sandbox Code Playgroud)

第二步是禁用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;
Run Code Online (Sandbox Code Playgroud)

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

-bool use_dex_cache = GetCompilerOptions().GetCompilePic();
+bool use_dex_cache = true;
Run Code Online (Sandbox Code Playgroud)

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

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

#ifdef HAVE_ANDROID_OS
#include "cutils/properties.h"
#endif
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

为了定位应用程序,我使用了一个设置目标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>()
[...]
Run Code Online (Sandbox Code Playgroud)

结束时:

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

沃利亚!

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

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

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