学习笔记:Android Xposed 框架入门
目录
- Xposed 框架简介
- Xposed 框架的具体使用
- 原理分析
Xposed 框架简介
Xposed是一款优秀的android java层 hook 框架。它允许你在不修改apk源码的情况下,通过编写自己的模块来改变apk的行为。它的优点是采用了插件机制,模块能够适用不同版本的框架和rom。模块改变apk行为的操作发生在内存中,对源apk不进行任何修改。你只需要安装编写的模块并重启相应的设备即可。
xposed 主要由三个项目来组成的:
- Xposed:Xposed的C++ 部分,主要是用来替换 /system/bin/app_process,并为XposedBridge 提供 JNI方法。
- XposedBridge:Xposed 提供的jar文件,app_process 启动过程中会加载该jar包,其他的 Modules 的开发都是基于 该jar包。
- XposedInstaller:Xposed的安装包,提供对基于Xposed框架的Modules的管理。
xposed 目前已逐步支持 ART虚拟机,兼容 android 5.0 以上版本。
Xposed 框架的具体使用
具体的安装教程需要自行百度,的确是安装环境比较耗费时间,不过都是躲不掉的!!!这里用一个绕过密码登录的例子进行说明。Android studio的配置代码这里就不贴了,需要的话可以去百度一下,很多资源。以下是安装使用教程:Xposed框架安装使用教程。
1. 创建 Android 工程
一个 Xposed 模块本质上是一个正常的 apk,所以你只需要去创建一个空的工程,不需要添加任何的 Activity。
2. 添加 jar 包
下载地址:jar包
在添加依赖项的时候注意使用 provided 不要使用 compile! 如下图红线部分所示:
然后还需要在 AndroidManifest.xml 文件配置,在Application节点下添加以下信息:
3. 在 assets 文件夹下写入入口信息
在 Android 工程的 main 文件夹下创建 assets 文件夹(没有的话)。XposedBridge 从assets 目录中的xposed_init 文件中获取入口点。xposed_init文件中每行配置一个进入点,使用完全限定名。在该例子中,进行如下配置。
4. 模块实现
-
在模块中创建一个类,根据自己的需求实现以下的接口:
- 安卓系统启动的时候(使用 IXposedHookZygoteInit 接口)
- 一个新的app被加载的时候(使用 IXposedHookLoadPackage 接口)
- 一个资源被初始化的时候( 使用 IXposedHookInitPackageResources 接口)
指定要 hook 的包名(这里是 com.markable.androidsecurity ),也就是你想要 hook 的应用包名,我这里就是本项目。
判断当前加载的包是否是指定的包(在接口方法中判断)
指定要 hook 的方法名
实现beforeHookedMethod方法和afterHookedMethod方法( hook 的具体功能)
以下是绕过登录的完整代码。
package com.markable.androidsecurity.module;
import android.widget.TextView;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
/**
* Created by Markable on 2018/7/11.
* Github: https://github.com/ddz-mark
* Info:
*/
public class SkipLoginCheck implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam param) throws Throwable {
// 判断是否是应用的包名
if (param.packageName.equals("com.markable.androidsecurity")) {
findAndHookMethod("com.markable.androidsecurity.LoginActivity", param.classLoader, "login", String.class, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("开始劫持了~");
XposedBridge.log("参数1 = " + param.args[0]);
XposedBridge.log("参数2 = " + param.args[1]);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedBridge.log("劫持前返回的result: " + param.getResult());
param.setResult(true);// 绕过登录
XposedBridge.log("劫持后返回的result: " + param.getResult());
XposedBridge.log("hook finish");
}
});
}
}
}
以上是绕过登录的功能代码,核心代码在于 param.setResult(true),可以让登录直接通过(这里是简单的登录模拟功能)。
5. 原理分析
- Zygote 进程简析
以下是应用此篇博客:Android Hook框架Xposed原理与源代码分析
在 Android 系统中,应用程序进程以及系统服务进程 SystemServer 都是由 Zygote 进程孵化出来的,而 Zygote 进程是由 Init 进程启动的, Zygote 进程在启动时会创建一个 Dalvik 虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个 Dalvik 虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的 Dalvik 虚拟机实例,这也是 Xposed 选择替换 app_process 的原因。
Zygote进程在启动的过程中,除了会创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,以及注册一些Android核心类的JNI方法来前面创建的Dalvik虚拟机实例中去。注意,一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库。这也就是可以将XposedBridge这个jar包加载到每一个Android应用程序中的原因。XposedBridge有一个私有的Native(JNI)方法hookMethodNative,这个方法也在app_process中使用。这个函数提供一个方法对象利用Java的Reflection机制来对内置方法覆写。具体的实现可以看下文的Xposed源代码分析。
- 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