android hook研究

XPOSED

Java正常执行调用逻辑

dvmCallVoidMethod

dvmCallMethod

dvmCallMethodV

dvmInterpret

dvmMterpStd

dvmMterpStdRun

循环

HANDLE_OPCODE(OP_INVOKE_STATIC)

GOTO_invoke(invokeStatic)

GOTO_invokeMethod

dvmMterp_invokeMethod

GOTO_TARGET(invokeMethod)

Pc=methodToCall->insns/methodToCall->nativeFunc

 

用反射的调用逻辑:

Constructor getDeclaredConstructor = clazz.getDeclaredConstructor()

Method m = clazz.getDeclaredMethod()

m.Invoke()

---->

 

Constructor. newInstance=>

Dalvik_java_lang_reflect_Constructor_constructNative=>

dvmInvokeMethod =>

method->nativeFunc/dvmInterpret insns

 

Method.invoke =>

Method.invokeNative =>

Dalvik_java_lang_reflect_Method_invokeNative=>

dvmInvokeMethod =>

method->nativeFunc/dvmInterpret

 

nativeFunc => dvmResolveNativeMethod

dfunc = dvmLookupInternalNativeMethod

dfunc()

 

GOTO_TARGET(invokeMethod, boolmethodCallRange, const Method* _methodToCall, u2 count, u2 regs)

 

Hook方式:

1.      静态修改apk中的函数调用,插入语句(droidbox)

2.      用反射获取java函数Method类型对应c层结构Method*,修改insns域的dex字节码(未发现)

3.      修改method->nativeFunc域,自己实现dvmResolveNativeMethod以重新映射(xposed/substrate)

 

 

 

 

 

 

原始系统启动过程

 

ART虚拟机和DALVIK虚拟机对比

1、Ahead-of-time (AOT)compilation instead of Just-in-time (JIT)

2、Improved garbagecollection

3、Improved memoryusage and reduce fragmentation

 

Ahead-of-time (AOT) compilation instead ofJust-in-time (JIT)

 

在dalvik中(实际为android2.2以上引入的技术),如同其他大多数jvm一样,都采用的是jit来做及时翻译(动态翻译),将dex或odex中并排的dalvik code(或者叫smali指令集)运行态翻译成native code去执行.jit的引入使得dalvik提升了3~6倍的性能

 

而在art中,完全抛弃了dalvik的jit,使用了aot直接在安装时用dex2oat将其完全翻译成native code.这一技术的引入,使得虚拟机执行指令的速度又一重大提升

 

Improved garbage collection

 

Xposed特点

1兼容性方面,支持selinux,对某些厂商做适配

2提供多种hook功能,可以hook系统api,应用app函数,资源操作,am pm等调用,插件扩展方便

3开源,不断更新,支持art虚拟机,android5.x/6.x

 

 

Hook类型

Xpose 支持4种hook类型:package:robv.android.xposed

IXposedHookZygoteInit  

        在Zygote进程启动初始化时执行hook,framework/system

IXposedHookLoadPackage

        在包加载时刻hook,此时可以hook用户app的函数

IXposedHookInitPackageResources

        在资源操作时刻回调

IXposedHookCmdInit

        在执行am,pm等时回调

de.rovb.android.xposed.XposedBridge.main          XposedBridge.java

        initForZygote:

        findAndHookMethod      handleBindApplication            在进程首次加载apk时hook

        findAndHookMethod      initAndLoop/systemMain       system_server启动时初始化hook

        hookAllConstructors       LoadedApk.class                      在app进程中加载多个apk

        loadModules:

                 IXposedHookZygoteInit           initZygote()

                 IXposedHookLoadPackage      加入sLoadedPackageCallbacks表

IXposedHookCmdInit               initCmdApp()

 

Xposed文件结构

Xposed => app_process

        Xposed_service.cpp         提供文件操作接口(libc.so尚未加载)

        Libxposed_dalvik.cpp      提供dalvik虚拟机hook接口(jni层实现)

        Libxposed_art.cpp           提供art虚拟机hook接口(jni层实现)

        Libxposed_command.cpp提供其他通用接口

        Xposed.cpp                      c++层一些实现,用于app_main

        App_main.cpp                 app_process入口,用于替换app_process(重写main和AppRuntime)  api>=21

        App_main2.cpp               app_process入口,用于替换app_process(重写main和AppRuntime)  api<21

Xposedbridge => xposedbridge.jar

        XposedHelpers.java         提供反射能力

        XposedBridge.java           xposedbridge入口,代替系统ZygoteInit类,主要的hook逻辑

        XC_MethodHook.java     为插件提供hook接口(java层实现)

 

问题1:何时加载framework.jar?

ActivityThread.class => const-classActivityThread => OP_CONST_CLASS.cpp

dvmResolveClass

        dvmFindClassNoInit

                 findClassFromLoaderNoInit

                         dvmCallMethod(loadClass)

                 dvmFindSystemClassNoInit

                         findClassNoInit

                                  searchBootPathForClass

java.boot.class.path        bootClassPath

/system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar

 

问题2:为何IXposedHookZygoteInit可以hook framework/system api

XposedBridge.main

        initForZygote

                 findAndHookMethod              加载系统jar

        loadModules

                 loadModule    

                         moduleInstance.initZygote()

                         hookLoadPackage(moduleInstance)

 

问题3:为何IXposedHookLoadPackage可以hook app自身函数

首次加载:

initForZygote   beforeHookedMethod    handleBindApplication

Process.start    “android.app.ActivityThread”

android.app.ActivityThread.main

android.app.ActivityThread.attach

ActivityManagerService. attachApplication

ActivityManagerService. attachApplicationLocked

android.app.ActivityThread.bindApplication

handleBindApplication

                                  ActivityStack.realStartActivityLocked

                                          scheduleLaunchActivity

                                                  getPackageInfoNoCheck

 

getPackageInfoNoCheck

        LoadedApk()构造

                 LoadedApk.getClassLoader

                         ApplicationLoaders.getDefault().getClassLoader(zip,libpath,null)

 

问题4:何时加载xposedbridge?

Xposed app_main->main():

        xposed::initialize()   -> env中加入xposedbridge.jar

        runtime.start(“de.robv.android.xposed.XposedBridge”)-> AndroidRuntime

                 startVm()

                 onVmCreated()

                         xposed::onVmCreated    加载xposedbridge.jar

                 startReg()

                 XposedBridge.main()

 

问题5:底层实现hook?

强制吧函数设置为native,并修改native函数使其返调java函数  libxposed_dalvik.cpp

   hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(),env->NewGlobalRef(reflectedMethodIndirect));

   hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(),env->NewGlobalRef(additionalInfoIndirect));

        SET_METHOD_FLAG(method,ACC_NATIVE);//设置Method->AccessFlag强制为native函数

   method->nativeFunc = &hookedMethodCallback;//修改默认回调dvmResolveNativeMethod为自定义函数,该函数原先从系统函数和so中的jni函数中寻找java对应的c层方法,hookedMethodCallback函数则调用dvmCallMethod执行java层方法

   method->insns = (const u2*) hookInfo;//该域原用于非native模式下保存dex字节码用于解释执行,现用于存储Method指针

   method->registersSize = method->insSize;

method->outsSize= 0;

 

 


SUBSTRATE

特点:

1 hook时机早,因此支持hook so函数

2重定向liblog.so,较易适配

3支持ios、android平台

4不支持dalvik,不支持selinux,不支持api>=5.0

 

文件:

substrate.h             //c++ header file used in JNIlayer hook

substrate-api.jar       //import package used in java layer hook

substrate-bless.jar     //used to removeproperties(private,protect,etc...) in java layer hook

com.saurik.substrate.apk//host apk, we canonly develop plugin for it to install package

\lib\armeabi  \lib\x86 //real operation for hooking

libAndroidBootstrap0.so //used to fake/system/lib/liblog.so and pull up libAndroidLoader.so   

libAndroidLoader.so     //used to pull all *.cy.so

        //MSLoadExtensions

libAndroidCydia.cy.so   //still in research

libDalvikLoader.cy.so   //still in research

libsubstrate.so         //provide jni layer hook low-level api

        //MSFindSymbolMSGetImageByName MSCloseFunction MSDebug MSHookFunction

libsubstrate-dvm.so     //provide java layer hook low-level api

        //MSDecodeIndirectReferenceMSJavaHookClassLoad MSJavaHookBridge MSJavaHookMethod

        //MSJavaCreateObjectKey MSJavaReleaseObjectKey MSJavaGetObjectKeyMSJavaSetObjectKey MSJavaBlessClassLoader

libSubstrateJNI.so      //used by substrate.apk to do c++ layerwork

        //getppidreadlink grep unlink symlink mkdir kill chown chmod

libSubstrateRun.so      //used by substrate.apk to dopatch/unpatch/link/unlink operation

        //patchunpatch link unlink nm rpl

update-binary.so        //used by substrate.apk to recoverpatch/link operation

 

Hook过程

 


Xposed框架介绍

Xposed框架分为xposed版app_process和XposedBridge.jar两部分。app_process就是zygote,我们先看看xposed版的zygote干了些什么。

4.1  Xposedzygote

注意,本章只分析32位,dalvik版的xposedapp_process,其入口main函数位于app_main.cpp里。

图14所示的代码展示了Xposed版zygote与众不同之处。

图14中,左上角的框是app_main的main函数,里边有两处不同之处:

·      AppRuntime:此处的AppRuntime类是xposed版的AppRuntime。它的与众不同之处从左下图的onVmCreated开始。当检查到isXposedLoaded为真是时,将调用xposed::onVmCreated函数。

·      xposed::onVmCreated函数位于右上框,它先判断当前虚拟机是ART还是dalvik,然后加载xposed一个特殊so,此处是/system/bin/libxposed_dalvik.so。注意,这里的xposed框架和XposedInstaller略有区别。因为XposedInstaller比较旧了(也就支持sdk 16的样子),还没有涉及到ART和dalvik的区别。在onVmCreate中,它将调用libxposed_dalvik.so中的xposedInitLib函数,然后再调用so中设置的onVmCreated函数。这个onVmCreated函数由xposedInitLib设置。

·      左上框中还有一个xposed:initialized函数。这个函数初始化了XposedShared类型的全局变量xposed,同时还把一个重要的jar包加到了CLASSPATH里。来看代码。如图15所示:

图9展示了initialize函数的内容,主要是最后一个把/system/bin/XposedBridge.jar(这个jar包的位置和我们在XposedInstaller那里看到得不同,原因前面解释过了)加到CLASSPATH比较重要。

注意,我们这里虽然对initialize介绍的内容很少,但实际上这个函数要真正看明白还是很需要技术实力的:

1  logcat:start:里边fork了logcat进程用来存储xposed自己的log

2  service:startAll:为了完美支持selinux,这里的处理更是很有技巧。selinux是一个完整的知识体系,想彻底掌握它的童鞋请参考我的三部曲文章《深入理解SELinux SEAndroid 

1&2其实很真实得反映出xposed的作者在Android、Linux上水平很高,经验很丰富。

最后,如果xposed框架启用成功,那么zygote的入口类将由以前的com.android.internal.os.ZygoteInit变成de.robv.android.xposed.XposedBridge。

下面我们按照执行流程,把相关函数分析一遍:

·      AppRuntime的onVmCreated,它最终会导致libxposed_dalvik.so中的onVmCreated被调用。我们直接分析最后这个onVmCreated。

·      然后AppRuntime里会调用de.robv.android.xposed.XposedBridge.main函数。

4.2  onVmCreated

图16为代码:

onVmCreated比较简单了:

·      针对android/content/res/XResource&XTypedArray进行了一些处理,这是为将来资源替换时候做准备。以后我们碰到再说。

·      为XposedBridge注册JNI函数

xposed官方说MIUI大量用了xposed的东西,并且不共享,以后碰到MIUI的问题他们不再支持。Don't know what to say....

4.3  XposdBridge.main函数

main函数代码如图17所示:

main函数里有三个重要函数:

·      initNative:好好学习下

·      initForZygote:好好学习下

·      loadModules:加载App插件

4.3.1  initNative

initNative很重要,来看代码,如图18所示:

图18中,我们重点看一下callback_XposedBridge_initNative和register_natives_XResources这两个函数。这两个函数比较简单,我们统一放到图19中:

不多说了,没什么难度。

4.3.2  initForZygote

从这个函数开始,xposed就开始给系统一些关键函数挂钩子了。我们看看它怎么玩儿的。代码如图20所示:

图20中我在eclipse里用了代码缩略显示的方法,可知一共有五个框,分别hook了一些关键内容。我们从上到下一次分析。先来分析下Xposed框架提供的挂钩函数findAndHookMethod。

1  findAndHookMethod介绍

findAndHookMethod用来对指定类的指定函数进行挂钩。这个函数很重要,开发插件APP时用得最多。来看它的代码,如图21所示:

findAndHookMethod代码Java层面的逻辑还是比较好理解的:

·      首先是通过class相关的函数找到指定的函数对象。这里的查找需要指定函数所在的类,函数名,函数参数信息等,属于精确(exact)查找。注意,findAndHookMethod函数的最后一个参数必须是XC_MethodHook类型,即钩子对象的数据类型。

·      然后就是XposedBridge.hookMethod。

来看hookMethod,如图22所示:

由hookMethod可知,一个目标函数可以挂多个钩子,这些钩子由一个集合来存储。然后我们将转到JNI层去看看hookMethodNative干了什么事情。这才是hook的核心。代码如图23所示:

hookMethodNative完成了真正的挂钩处理,其思想很简单:

·      先找到目标函数在native层对应的Method对象。

·      修改这个Method为native方法,并设置nativeFunc为hookedMethodCallback函数。

·      然后还要保存原函数的一些信息到insns域。

我们在《深入理解Androiddalvik一文中介绍过,JVM调用java函数时候,发现这个函数为native的话,就调用它的nativeFunc。下面我们看看钩子函数的调用。

2  调用钩子函数

hook钩子函数后我们就要调用它。上一节我们发现xposed在挂钩子的时候会把原函数改造成native属性(即Dalvik会按native函数的方式调用它),对应的nativeFunc是hookedMethodCallback,其代码如图24所示:

注意喔,我们现在已经在处理被挂钩函数的调用了喔....从JNI会进入到java层的钩子函数dispatch总入口,及handleHookedMethod,这个函数比较复杂,我们一段一段来看它。

很简单。接着看第二段,如图26所示:

貌似也很简单喔...

现在来看原目标函数的调用,即invokeOriginalMethodNative。代码如图27所示:

同样很容易,不多说了。到此,我们已经看到了Xposed挂钩的所有过程。好像也没什么复杂的,只要对dalvik稍微属性点,应该是比较容易做的。

当然,本文只是给大家show了主要流程,真要自己动手做,我觉得难点在于参数传递等方面。anyway,了解了大体流程,后面的事情也好办,多尝试几次就好。

下面我们回过头来看initForZygote里加的几个钩子都干了什么。

3  initForZygote钩子设置

图20的initForZygote代码示意中可知xposed对下面几个函数进行hook(此处先不讨论钩子函数干了什么)

·      ActivityThread.handleBindApplication:这个函数是ActivityManagerService内部调用ActivityThread.bindApplication间接触发的。该函数的详情可参考《深入理解Android卷2》第六章深入理解ActivityManagerService的“ApplicationThread的bindApplication分析”一节。handleBindApplication主要工作是初始化APP(APP由zygote进程fork而来,在hanldeBindApplication之前,这个APP进程和zygote没什么区别。只有调用完handleBindApplication之后,这个APP进程才是APP,比如该进程有了对应的名字,Aplication对象被创建等)。

·      com.android.server.ServerThread. initAndLoop函数:这个函数是给system_server用的,用于启动系统各种重要服务。

·      LoadedApk构造函数:通过hookAllConstructors来对LoadedApk各种构造函数进行挂钩。LoadedApk是APK文件在APP里的代表对象,里边有很多重要的信息。

·      android.app.ApplicationPackageManager.getResourcesForApplication:挂钩PackageManager的资源获取函数。

·      hookResources:对资源进行hook。这一块由于涉及到android APP对资源的管控,以后有机会我们再详细介绍。

4.3.3  loadModules

main函数在initForZygote之后的下一个动作就是loadModules,这就是加载所有的插件APP。我们来看看这个函数,代码如图28所示:

我们来看下loadModules的处理:

·      先从XposedInstaller那获取当前已经安装(并启用)的插件APP列表,然后调用loadModule来加载这个APP。

·      loadModule从APP的assets/xposed_init文件里看看这个apk里声明了哪些钩子类。

·      loadModule校验这些钩子类是不是符合Xposed定义的钩子类类型,然后作对应处理。图29列出了Xposed支持的钩子类类型。

图30展示了hookLoadPackage和hookInitPackageResource两个函数的内容,特别简单。

嗯嗯,就是把对应的钩子函数保存起来先。

好了,下面我们就来开始分析initForZygote里挂上的几个钩子分别有什么用。

4.3.4  handleBindApplication钩子

initForZygote为hanldeBindApplication设置了前处理钩子,代码如图31所示:

前面说过,在APP生命周期内,handleBindApplication是APP刚准备好相关信息的一个重要点,在这个点去进行挂钩处理简直是最好不过了。当然,此处的挂钩处理也就是准备好这个APP的相关信息然后调用所有的IXposedHookLoadPackage类型的钩子。

注意,除了handleBindApplication之外,由于一个APP进程事实上可以加载多个APK(比如那些申明同样的uid和运行在同一进程的APP),在LoadedApk的构造函数中也做了类似的处理

IXposedHookLoadPackage钩子一般会干些什么呢?图31对XposedInstaller的处理就很明显了。一般而言,这种钩子会对目标APP中感兴趣的函数进行挂钩(调用findAndHookMethod),比如XposedInstaller对getActiveXposedVersion进行了挂钩,用于返回系统里正在使用的Xposed框架版本。

我们应该在钩子函数里干些什么?这是一个重要问题。我也不废话了,直接上XposedDemo的源码,如图32所示:

图32是我在xposed-learning项目中提供的XposedDemo示例,可知:

·      Xposed框架对handleBindApplication进行hook后,当有APP启动的时候,该框架就会调用所有IXposedHookLoadPackage类型的钩子。

·      这种钩子一定要区分当前被hook的APP是不是自己想要Hook的APP。如果不是,请直接return。如果是自己的目标APP,则进一步通过findAndHookMethod进行挂钩。

简单点说,我们在IXposedHookLoadPackage的handleLoadPackage中把该挂的钩子都挂上就好。

4.3.5  initAndLoop钩子

initAndLoop是system_server进程的关键函数,在这个函数里Android Framework的绝大部分Service都将被创建。真是艺高人胆大,这个进程居然都提供了挂钩处理。其代码如图33所示:

代码倒是很简单,无非是针对system_server进行hook。插件函数如果想区分被hook的进程是否为system_server的话,只需要判断packageName是否为"android"即可。

再次强调,system_server是Android Java层Framework的核心,要hook它需要万分小心,否则或导致手机系统出现会各种不稳定,崩溃,重启等情况。

下面我们介绍下Xposed框架对资源是怎么Hook的。

4.4  对资源的Hook

前面章节介绍了Xposed框架如何对代码调用逻辑进行hook。在Android APP中,除了代码逻辑外,Xposed还支持对资源进行Hook。对资源Hook的原理其实和对代码调用进行Hook的原理类似。这里我们简单介绍下Xposed框架如何对资源进行Hook。

在AndroidAPP中,资源有三个重要类:

·      ResourcesManager:一个APP进程有一个ResourcesManager。一个ResourcesManager可以管理多个Resouces。

·      Resources:Resources是一个APK中资源的代表,注意,它不是指单独的一种类型的资源(比如字符串,图片等),它是一个APK文件中资源文件的代表。Resources对象有一个AssetManager,AssetManager能操作APK文件(其实就是一个压缩包)assets以及res目录里的内容。

·      Resources通过ResourcesKey将自己保存到ResourceManager中的一个ArrayMap里。

·      对于资源挂钩来说,Xposed框架及插件APP做如下几件重要的事情:

·      和handleBindApplication类似,在APP进程某个时间点的时候(猜测:初始化资源相关模块)进行Hook,然后调用IXposedHookInitPackageResources类型的钩子。

·      IXposedHookInitPackageResources类型的钩子通过Xposed提供的XResources类的setReplacement对原有的资源进行替换。在Xposed框架中,XResources是用于替代Resources的。

·       App运行时候,Xposed框架截获相关的资源获取函数(比如Context的getString),先看看是否被replace了,如果是,则返回被replace的结果。

就这么简单。我们一步一步来看。

注意,XposedDemo并没有hook资源,请感兴趣的童鞋们自行加上该功能进行测试。

 

 

 

 

1.xposed框架一定会支持art框架以及安卓L

 

2.@Rovo89已经有在开发和测试支持art引擎的xposed框架,只是由于两个原因他没有发布,一个是由于需要经过大量的测试才行,第二是因为art本身还不够稳定,而且谷歌还在不停的修改和更新art引擎,而这些变化会导致xposed框架编码上会带来一些重构,同时还需要关注dual-stack 32/64 bit Zygote,以及严格SELinux的策略对xposed框架的影响。@Rovo89想等art引擎的正式版发布后再发布对于art的支持。

 

3.由于kitkat里的art测试版和android l里的art正式版是完全不同的环境,所以后续xposed框架将主要支持android l,如果测试和开发量不大的话,可能会考虑兼容kitkat里的art环境

 

待研究的点:

1.特殊情况hook,如隐藏java函数(@hide)及native函数测试

2.稳定性测试,包括api版本支持、厂商支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值