1.1 概述 Xposed 是 GitHUB 上 rovo89 大大设计的一个针对 Android 平台的动态劫持项目,通过替换 /system/bin/app_process 程序控制 zygote 进程,使得 app_process 在启动过程中会加载 XposedBridge.jar 这个 jar 包,从而完成对系统应用的劫持。 Xposed 框架的基本运行环境如下:因为 Xposed 工作原理是在 /system/bin 目录下替换文件,在 install 的时候需要root 权限,但是运行时不需要 root 权限。需要在 Android 4.0 以上版本的机器中2. GitHub 上的 Xposed 资源梳理一下,可以这么分类: XposedBridge.jar : XposedBridge.jar 是 Xposed 提供的 jar 文件,负责在 Native层与 FrameWork 层进行交互。 /system/bin/app_process 进程启动过程中会加载该jar 包,其它的 Modules 的开发与运行都是基于该 jar 包的。 Xposed : Xposed 的 C++ 部分,主要是用来替换 /system/bin/app_process ,并为 XposedBridge 提供 JNI 方法。 XposedInstaller : Xposed 的安装包,负责配置 Xposed 工作的环境并且提供对基于 Xposed 框架的 Modules 的管理。 XposedMods :使用 Xposed 开发的一些 Modules ,其中 AppSettings 是一个可以进行权限动态管理的应用 1.2 Mechanism :原理 1.2.1 Zygote 在 Android 系统中,应用程序进程都是由 Zygote 进程孵化出来的,而 Zygote 进程是由 Init 进程启动的。 Zygote 进程在启动时会创建一个 Dalvik 虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个 Dalvik 虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的 Dalvik 虚拟机实例。 Zygote 进程在启动的过程中,除了会创建一个 Dalvik 虚拟机实例之外,还会将 Java运行时库加载到进程中来,以及注册一些 Android 核心类的 JNI 方法来前面创建的 Dalvik 虚拟机实例中去。注意,一个应用程序进程被 Zygote 进程孵化出来的时候,不仅会获得 Zygote 进程中的 Dalvik 虚拟机实例拷贝,还会与 Zygote 一起共享 Java 运行时库。这也就是可以将XposedBridge 这个 jar 包加载到每一个 Android 应用程序中的原因。 XposedBridge 有一个私有的 Native ( JNI )方法 hookMethodNative,这个方法也在 app_process 中使用。这个函数提供一个方法对象利用 Java 的 Reflection 机制来对内置方法覆写。具体的实现可以看下文的 Xposed 源代码分析。 1.2.2 Hook/Replace Xposed 框架中真正起作用的是对方法的 hook 。在 Repackage 技术中,如果要对APK 做修改,则需要修改 Smali 代码中的指令。而另一种动态修改指令的技术需要在程序运行时基于匹配搜索来替换 smali 代码,但因为方法声明的多样性与复杂性,这种方法也比较复杂。 在 Android 系统启动的时候, zygote 进程加载 XposedBridge 将所有需要替换的 Method 通过 JNI 方法 hookMethodNative 指向 Native 方法 xposedCallHandler , xposedCallHandler 在转入 handleHookedMethod 这个 Java 方法执行用户规定的 Hook Func 。 XposedBridge 这个 jar 包含有一个私有的本地方法: hookMethodNative ,该方法在附加的 app_process 程序中也得到了实现。它将一个方法对象作为输入参数(你可以使用 Java 的反射机制来获取这个方法)并且改变 Dalvik 虚拟机中对于该方法的定义。它将该方法的类型改变为 native 并且将这个方法的实现链接到它的本地的通用类的方法。换言之,当调用那个被 hook 的方法时候,通用的类方法会被调用而不会对调用者有任何的影响。在 hookMethodNative 的实现中,会调用 XposedBridge中的handleHookedMethod这个方法来传递参数。 handleHookedMethod 这个方法类似于一个统一调度的 Dispatch 例程,其对应的底层的 C++ 函数是 xposedCallHandler 。而 handleHookedMethod 实现里面会根据一个全局结构 hookedMethodCallbacks 来选择相应的 hook函数,并调用他们的 before, after 函数。 当多模块同时 Hook 一个方法的时候, Xposed 会自动根据 Module 的优先级来排序,调用顺序如下: A.before - B.before - original method - B.after - A.after 2 源代码分析 2.1 Cpp 模块其文件分类如下: app_main.cpp :类似 AOSP 中的 frameworks/base/cmds/app_process/app_main.cpp,即/system/bin/app_process 这个 zygote 真实身份的应用程序的源代码。关于zygote 进程的分析可以参照 Android:AOSPCore 中的 Zygote 进程详解。 xposed.cpp :提供给 app_main.cpp 的调用函数以及 XposedBridge 的 JNI 方法的实现。主要完成初始化工作以及 Framework 层的 Method 的 Hook 操作。 xposed.h , xposed_offsets.h :头文件Xposed 框架中的 app_main.cpp 相对于 AOSP 的 app_main.cpp 中修改之处主要为区分了调用 runtime.start() 函数的逻辑。 Xposed 框架中的 app_main.cpp 在此处会根据情况选择是加载 XposedBridge 类还是 ZygoteInit 或者 RuntimeInit 类。而实际的加载 XposedBridge 以及注册 JNI 方法的操作发生在第四步: xposedOnVmCreated中。 1.包含 cutils/properties.h ,主要用于获取、设置环境变量, xposed.cpp 中需要将XposedBridge 设置到 ClassPath 中。 2.包含了 dlfcn.h ,用于对动态链接库的操作。 3.包含了 xposed.h ,需要调用 xposed.cpp 中的函数,譬如在虚拟机创建时注册 JNI 函数。 4.增加了 initTypePointers 函数,对于 Android SDK 大于等于 18 的会获取到 atrace_set_tracing_enabled 函数指针,在 Zygote 启动时调用。 5.AppRuntime 类中的 onVmCreated 函数中增加 xposedOnVmCreated 函数调用。 6.源代码中的 Log* 全部重命名为 ALog*, 所以 Logv 替换为 Alogv ,但是功能不变。 7.Main 函数开始处增加了大量的代码,但是对于 SDK 版本小于 16 的可以不用考虑。2.1.1 Main 函数: zygote 入口 int main(int argc, char* const argv[])
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、微信小程序、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了洛宁免费建站欢迎大家使用!
几天前,在看雪安卓版块的论坛上,看到有人发表了一份dalvik hook的实现代码,这里小弟果断献丑下,给提供一份dalvik hook的另外一种实现。
首先解释下dalvik虚拟机中的Method结构体,Method结构体声明在源码目录树下的dalvik/vm/oo/Object.h文件内,在dalvik的世界中,每一个java方法都有一个对应的Method对象,
dalvik虚拟机在执行java方法时,就会通过该方法的Method对象调用到方法的具体实现代码,Method结构体内有个成员,名位insns,该字段保存着java方法具体的实现代码。那么我今天提供的这个dalvik hook的实现原理就是通过修改Method对象中的insns字段的值来达到hook java的目的。
这里简短的用文字的形式描述下Hook的实现过程。
现在有一个返回字符串的函数原型声明为:
public String truth()
{
return "hello from truth";
}
另外声明一个方法为:
public String fake()
{
return "fake string";
}
之后,该类被加载后,分别获得truth和fake方法的Method对象,并将fake方法的实现代码赋值给truth的Method对象,之后,应用程序调用truth方法时,获得的字符串为"fake string"。
前面插件化一和二说了下插桩式加载未安装的APK,主要是重写了getResource和getClassloader两个方法来实现的。以及每个组件要实现一个接口,通过接口注入上下文来达到它的生命周期。
那么插桩式和hook式的实现方式有什么不同呢?
插桩式是怎么加载到插件中的class文件呢,是通过将将APK转化成插件的Classloader,然后想要加载插件的class文件,我们的去拿这个插件的classloader去loadClass。所以是有一个中间者的。
hook式呢是将插件apk融入到了我们的宿主apk,那直接在里面就可以直接loadClass了,在不用这个插件的ClassLoader了,这样的话对于插件和宿主就没什么区别了,不像插桩式有一个中间者。
那么要实现hook式 就要知道android中一个class文件式怎样被加载到内存中去的。其实就是通过PathClassLoader来加载的。
那么我们先看下ClassLoader
任何一个java程序都是由一个或者多个class组成的,在程序运行时,需要将class文件加载到JVM中才可以使用,负责加载这些class文件的就是java的类加载机制。CLassLoader的作用就是加载class文件提供给程序运行时使用,每个Class对象内部都有一个ClassLoader来标示自己是有那个classLoade加载的。
Android app的所有的java文件都是通过PathClassLoader来加载的,那么它的父类是BaseDexClassLoader,还有一个兄弟类是DexClassLoader,那么他们有什么区别呢。
从上面可以看出这两个类的构造函数不同。(在26的源码中DexClassLoader中的optimizedDirectory也废弃了)
PathClassLoader:用于Android应用程序类加载器。可以加载指定的dex,以及jar、zip、apk中的classes.dex
DexClassLoader:加载指定的dex以及jar、zip、apk中的classes.dex。
可以看到创建ClassLoader的时候需要接收一个CLassLoader parent的参数,这个parent的目的就在于实现类加载的委托。
某个类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,一次递归,如果父加载器可以完成加载任务,那么就返回,只有当父加载器无法完成加载任务时,才自己去加载。
因此我们自己创建的ClassLoader:newPathClassLoader("/sdcard/xx.dex",getClassLoader()),并不仅仅只能加载我们的xx.dex中的class。
需要注意的是,findBootstrapClassOrNull 这个方法,当parent为null的时候,去这个BootCLassLoader进行加载,
但是在Android当中的实现:
所以new PathClassLoader("/sdcard/xx.dex",null),是不能加载Activity.class的。
上面分析了加载了一个class,是利用了双亲委托机制,那么要是都找不到那就开始调用自己的findCLass方法
在ClassLoader类中findClass:
任何ClassLoader的子类,都可以重写loadClass和findClass。如果你不想使用双亲委托,就重写loadClas修改实现,重写findClass则表示在双亲委托机制下,父ClassLoader都找不到class的情况下,定义自己去查找一个class。
而我们的PathClassLoader会自己负责加载Activity这样的类,利用双亲委托父类去加载activity,而我们的PathClassLoader没有重写findClass,是在它的父类里面。因此我们可以看看父类的findClass是如何实现的。
可以看到加载PathClassLoader加载class,转化为从DexPathList中加载class了,那么我们看看DexPathList中的findClass
那么从上面分析得到
到这里我们想要加载一个插件的apk ,其实最终加载的是一个dex文件(先说class文件,加载资源后面说),有没有办法吧这个dex文件给转化成一个 Element 对象,给放到 Elemeng数组 当中,这样直接就可以加载我们插件中的类了。
1、首先我们肯定是要得到插件APK的的中DexPathList对象中的dexElement数组
2、插件的dexElements数组我们拿到了,那么是不是要开始拿我们系统里面的 ,我们反射获取,和上面的一样。
3、上面我们获取到了系统和我们插件的dexElement数组,然后我们将这个数组合并到一个新的数组里面去,并且给注入到系统里面
至此,加载插件的一个流程基本就完成了。但是上面只是处理了class文件,没有处理资源。资源的话我们也是采用hook的方式去实现
在宿主的Application中hook这个方法,然后去重写getAsserts和getResources两个方法:
然后在插件的BaseActivity中继续重写getAssets和getResources两个方法
这样就可以完成hook式加载一个未安装的APK了。至此基本就完成了插桩式和Hook式插件化的基本实现。(后面几篇是优化)。
1.向目标进程注入代码(注入so,并调用该so里的一个函数)。首先调用ptrace函数,调试com.android.browser进程,在这里我们需要遍历该进程加载的libc.so,这里有我们需要的dlopen,dlsym等函数的地址,我们先中断com.android.phone,修改其寄存器...
1、Xposed:Java层的HOOK框架,由于要修改Zgote进程,需要Root;
2、CydiaSubstrator:本地层的HOOK框架,本质上是一个inline Hook;
3、dexposed框架
4、AndFix框架;
5、Sophix 框架;
6、AndroidMethodHook框架;
7、Legend框架:在AndFix框架的基础上,在方法进行替换前进行了方法的备份;
8、YAHFA框架;
9、EPIC框架;
10、VirtualXposed:Virtual APP与Xposed的一个结合。
扩展资料
使用框架的原因
因为软件系统很复杂,特别是服务器端软件,涉及到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮助完成一些基础工作,只需要集中精力完成系统的业务逻辑设计。
而且框架一般是成熟,稳健的,可以处理系统很多细节问题,比如,事务处理,安全性,数据流控制等问题。
还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,可以直接享受别人升级代码带来的好处。框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。
框架开发
框架的最大好处就是重用。面向对象系统获得的最大的复用方式就是框架,一个大的应用系统往往可能由多层互相协作的框架组成。
由于框架能重用代码,因此从一已有构件库中建立应用变得非常容易,因为构件都采用框架统一定义的接口,从而使构件间的通信简单。
框架能重用设计。它提供可重用的抽象算法及高层设计,并能将大系统分解成更小的构件,而且能描述构件间的内部接口。
这些标准接口使在已有的构件基础上通过组装建立各种各样的系统成为可能。只要符合接口定义,新的构件就能插入框架中,构件设计者就能重用构架的设计。
框架还能重用分析。所有的人员若按照框架的思想来分析事务,那么就能将它划分为同样的构件,采用相似的解决方法,从而使采用同一框架的分析人员之间能进行沟通。
参考资料来源:百度百科-框架
本文名称:包含hookandroid的词条
文章链接:https://www.cdcxhl.com/article10/dsdopgo.html
成都网站建设公司_创新互联,为您提供标签优化、域名注册、微信公众号、网站维护、网站收录、品牌网站建设
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联