Xposed的框架的使用
2017-02-16 12:19
239 查看
一、Xposed简介
Xposed是一款优秀的android java层 hook 框架。它允许你在不修改apk源码的情况下,通过编写自己的模块来改变apk的行为。它的优点是采用了插件机制,模块能够适用不同版本的框架和rom。模块改变apk行为的操作发生在内存中,对源apk不进行任何修改。你只需要安装编写的模块并重启相应的设备即可。二、相关资源
Xposed 官网:http://repo.xposed.info/Xposed 项目 github 地址:https://github.com/rovo89
Xposed 官方教程 :https://github.com/rovo89/XposedBridge/wiki/Development-tutorial
Xposed Api 之XposedBridge.jar 下载:https://jcenter.bintray.com/de/robv/android/xposed/api/
三、使用Xposed
1.安装Xposed 框架
首先你需要在手机设备上安装Xposed 框架,官网上给我们提供了一个apk 来完成框架的安装。Xposed Installer官方下载:http://repo.xposed.info/module/de.robv.android.xposed.installer
由于Android系统5.0以上默认采用了ART模式,Android系统5.0 以下默认采用Davik模式。所以目前Xposed框架主要有两个版本:一个是Android 4.0.3 to Android 4.4版本,一个是Android 5.0 以上版本。
我们以Android 4.0.3 to Android 4.4版本为例。
下载Xposed installer 成功后,将de.robv.android.xposed.installer.apk安装在海马模拟中或者已经root真机上
选择 框架 ,可看到app_process和XposedBridge.jar需要激活
选择 安装/更新,会提示重启
重启后,会发现app_process和XposedBridge.jar已经激活。
2.编写相关的模块
现在我们来编写Xposed模块。1.创建一个Android 工程
一个Xposed 模块本质上是一个正常的apk,只是这个apk没有与用户交互的Activity界面,它仅仅包含一些mete 数据和文件,并且该apk安装后在桌面应用中没有图标显示。所以你只需要创建一个空的安卓工程,不需要添加任何Actitity.2. 将Android 工程变成Xposed模块
在工程中添加 Xposed Framework API
Xposed模块为了使用Xposed框架的api ,需要下载相应的 XposedBridge.jar 包。Android Studio (Gradle-based)
Xposed Framework API 在下面网址上可进行查看:
https://bintray.com/rovo89/de.robv.android.xposed/api
在 Androdri Studio 工程中
app/build.gradle文件中添加依赖项:
repositories { jcenter(); } dependencies { provided 'de.robv.android.xposed:api:53' }
重要的事情说三遍!使用 provided 不要使用 compile! compile 会将整个 API 类 编译进你的apk中而导致出问题。provided 则只是提供了API 类的引用,API 类真正的实现则在 Xposed FramWork中。
在大多数情况下,
repositories已经存在,并且已经有一些依赖,所以只需要将
provided这一行添加到存在的
dependcies模块中即可。
如果 需要查看api资源,则添加下面的两行:
provided 'de.robv.android.xposed:api:53' provided 'de.robv.android.xposed:api:53:sources'
确保关闭Instant Run项
File -> Settings -> Build, Execution, Deployment -> Instant Run,否者你的apk中将不包含这些类
Eclipse
Eclipse创建的工程需要创建相应的jar包,jar包地址下载:https://jcenter.bintray.com/de/robv/android/xposed/api/
推荐将下载的
api-XX.jar包放入到
lib子目录中,不要将jar包放入到
libs子目录中。因为
libs目录中的jar包会使Eclipse将API类编译到APK中,而事实上API只需要引用即可。
右击
api-XX.jar文件选择
Build Path -> Add to Build Path即可
Xposed Framework API
android 5.0以前的版本,推荐使用API 53
http://api.xposed.info/reference/packages.html
AndroidManifest.xml 文件配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.robv.android.xposed.mods.tutorial" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <meta-data android:name="xposedmodule" android:value="true" /><!--应用为模块--> <meta-data android:name="xposeddescription" android:value="Easy example which makes the status bar clock red and adds a smiley" /><!--模块描述--> <meta-data android:name="xposedminversion" android:value="53" /> <!--版本信息--> </application> </manifest>
模块实现
在模块中创建一个类,并命名为 ’Tutorial‘,该类放在de.robv.android.xposed.mods.tutorial 包中。类名和包名是可以任意取的。package de.robv.android.xposed.mods.tutorial; public class Tutorial { }
教程的第一步,我们只需要将加载的模块信息打印出来。
一个模块有一些进入点,这些进入点的选择取决于你想修改的位置。你可以让Xposed框架调用你模块中的函数在下面几个位置:安卓系统启动的时候(使用 IXposedHookZygoteInit 接口)、一个新的app被加载的时候(使用 IXposedHookLoadPackage 接口)、一个资源被初始化的时候( 使用 IXposedHookInitPackageResources 接口)。
所有的进入点 都是 XposedMod 接口的子接口。在本示例中,进入点是“一个新的app被加载的时候”,我们需要实现
IXposedHookLoadPackage 接口。事实上该接口只有一个带有一个参数的方法,该参数中给出了加载的应用的Context 信息:
package de.robv.android.xposed.mods.tutorial; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; public class Tutorial implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { XposedBridge.log("Loaded app: " + lpparam.packageName); } }
log方法将写信息到标准的ogcat中(
/data/data/de.robv.android.xposed.installer/log/debug.log)
assets/xposed_init
XposedBridge 从assets目录中的
xposed_init文件中获取入口点。
xposed_init文件中每行配置一个进入点,使用完全限定名。在该例子中,进行如下配置
de.robv.android.xposed.mods.tutorial.Tutorial.
3.运行模块
在设备上安装该模块,安装成功后看不到模块的界面。第一次安装该模块后,你需要在Xposed Install app中启动模块。打开Xposed Install app,选择“Modules”tab 后。勾选模块,并重启Android系统。这时你打开Xposed Install app 日志模块,会发现下面的输出:
四、替换资源
替换Boolean, Color, Integer, int[], String and String[]类型的简单资源
1.替换系统框架(Android Framwork)资源
替换系统框架资源(对所有app 起作用)需要实现IXposedHookZygoteInit接口的
initZygote方法,并在该方法中调用
Resources.setSystemWideReplacement(...)方法替换资源
package de.robv.android.xposed.mods.tutorial; import android.content.res.XResources; import de.robv.android.xposed.IXposedHookZygoteInit; public class Tutorial2 implements IXposedHookZygoteInit{ @Override public void initZygote(StartupParam arg0) throws Throwable { XResources.setSystemWideReplacement("android", "bool", "config_unplugTurnsOnScreen", false); } }
2.替换app应用资源
替换app应用资源需要实现IXposedHookInitPackageResources类的
andleInitPackageResources方法,并在该方法中调用
res.setReplacement(...)方法替换资源,注意在该方法中不要使用
XResources.setSystemWideReplacement方法
package de.robv.android.xposed.mods.tutorial; import android.content.res.XResources; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import de.robv.android.xposed.IXposedHookInitPackageResources; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; public class Tutorial3 implements IXposedHookInitPackageResources { @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { //只替换systemui应用的资源 if (!resparam.packageName.equals("com.android.systemui")) return; // 替换资源的不同方式 resparam.res.setReplacement(0x7f080083, "YEAH!"); // WLAN toggle text. You should not do this because the id is not fixed. Only for framework resources, you could use android.R.string.something resparam.res.setReplacement("com.android.systemui:string/quickpanel_bluetooth_text", "WOO!"); resparam.res.setReplacement("com.android.systemui", "string", "quickpanel_gps_text", "HOO!"); resparam.res.setReplacement("com.android.systemui", "integer", "config_maxLevelOfSignalStrengthIndicator", 6); resparam.res.setReplacement("com.android.systemui", "drawable", "status_bar_background", new XResources.DrawableLoader() { @Override public Drawable newDrawable(XResources res, int id) throws Throwable { return new ColorDrawable(Color.WHITE); } });//你不能直接使用Drawble类进行替换,因为Drawble类可以影响其他引用Ddrawble类实例的ImageView ,最好使用一个包装器。 } }
替换复杂的资源
对于复制的资源,如动画资源 ,我们也能够替换,下面我们来替换battery icon动画资源布局
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true" > <item android:drawable="@drawable/icon1" android:duration="150"></item> <item android:drawable="@drawable/icon2" android:duration="150"></item> <item android:drawable="@drawable/icon3" android:duration="150"></item> <item android:drawable="@drawable/icon4" android:duration="150"></item> <item android:drawable="@drawable/icon5" android:duration="150"></item> <item android:drawable="@drawable/icon6" android:duration="150"></item> </animation-list>
代码:
package de.robv.android.xposed.mods.tutorial; import com.example.xposedmoduletest.R; import android.content.res.XModuleResources; import de.robv.android.xposed.IXposedHookInitPackageResources; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; public class Tutorial4 implements IXposedHookZygoteInit, IXposedHookInitPackageResources { private static String MODULE_PATH = null; @Override public void initZygote(StartupParam startupParam) throws Throwable { MODULE_PATH = startupParam.modulePath; } @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { if (!resparam.packageName.equals("com.android.systemui")) return; XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res); resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery", modRes.fwd(R.drawable.animation)); resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery_charge", modRes.fwd(R.drawable.animation)); } }
Xposed框架会将模块请求资源的请求指向你模块中的资源
替换的效果:
替换布局
你可以用替换资源的方法来替换布局文件,但这样你不得不将目标apk中的整个layout文件复制出来进行修改,这样会使模块的Rom兼容性降低。并且如果两个以上的模块修改布局后,最后修改布局的模块会起作用。更重要的是,布局中指向其它资源的ID很难确定下来。推荐使用下面的方法修改布局:package de.robv.android.xposed.mods.tutorial; import android.graphics.Color; import android.widget.TextView; import de.robv.android.xposed.IXposedHookInitPackageResources; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; import de.robv.android.xposed.callbacks.XC_LayoutInflated; import de.robv.android.xposed.callbacks.XC_LayoutInflated.LayoutInflatedParam; public class Tutorial5 implements IXposedHookInitPackageResources{ @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { if (!resparam.packageName.equals("com.android.systemui")) return; resparam.res.hookLayout("com.android.systemui", "layout", "status_bar", new XC_LayoutInflated() { @Override public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable { TextView clock = (TextView) liparam.view.findViewById( liparam.res.getIdentifier("clock", "id", "com.android.systemui")); clock.setTextColor(Color.RED); XposedBridge.log("layout resNames.fullname:"+liparam.resNames.fullName); XposedBridge.log("layout resNames.id:"+liparam.resNames.id); XposedBridge.log("layout resNames.name:"+liparam.resNames.name); XposedBridge.log("layout resNames.pkg:"+liparam.resNames.pkg); XposedBridge.log("layout resNames.type:"+liparam.resNames.type); XposedBridge.log("layout resNames.variant:"+liparam.variant); XposedBridge.log("layout resNames.view:"+liparam.view); } }); } }
回调方法
handleLayoutInflated会在layout文件被填充后回调,在方法的 LayoutInflatedParam 对象 参数中,你可以找到你想修改的View组件。你也可以通过调用
resNames来确定加载的那一个布局文件。用
variant来确定加载的布局的目录’layout-land‘。
res同时也会帮你获取资源的ID和其它的资源。
五、用反射来hook方法
每当应用加载的时候,IXposedHookLoadPackPage接口的handLoadPackage方法就会被调用执行,为了让我们在正确的进程中执行,需要先判断被加载的包是不是正确的包package de.robv.android.xposed.mods.tutorial; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; public class Tutorial6 implements IXposedHookLoadPackage { @Override public void handleLoadPackage(LoadPackageParam param) throws Throwable { if(!param.packageName.equals("com.android.systemui")) return; } }
一旦我们进入到正确的进程进后,我们就能用param变中的ClassLoad来访问该进程中加载的类
package de.robv.android.xposed.mods.tutorial; import android.webkit.WebView.FindListener; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodHook.MethodHookParam; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; public class Tutorial6 implements IXposedHookLoadPackage { @Override public void handleLoadPackage(LoadPackageParam param) throws Throwable { if(!param.packageName.equals("com.android.systemui")) return; findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method } }); } }
XposedHelpers是一个重要的工具类,推荐用Eclipse的同学静态导入该类中的方法
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;。该类能够通过反射机制来访问方法、构造器、域。
findAndHookMehthod(String packageName,Class<?> clazz, String methodName, Object... args))方法来对函数进行Hook。如果在方法前和方法后Hook,该方法最后一个参数需要实现
XC_MethodHook类的
beforeHookedMethod和
afterHookedMethod方法,如果想要替换整个方法,则需要实现
XC_MethodReplacement类的
replaceHookedMethod方法
XposedBridge保存了每个Hook方法的回调方法。优先级高的回调方法被优先调用
A.before -> B.before -> original method -> B.after -> A.after
package de.robv.android.xposed.mods.tutorial; import android.graphics.Color; import android.webkit.WebView.FindListener; import android.widget.TextView; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodHook.MethodHookParam; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; public class Tutorial6 implements IXposedHookLoadPackage { @Override public void handleLoadPackage(LoadPackageParam param) throws Throwable { if(!param.packageName.equals("com.android.systemui")) return; findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { TextView tv = (TextView) param.thisObject;//获取调用该方法类的对象 String text = tv.getText().toString(); tv.setText(text + " :)"); tv.setTextColor(Color.RED); } }); } }
相关文章推荐
- 以字传情
- CSS+JS笔记
- (转) 深度增强学习与通用人工智能
- 2017浅谈面试(一)
- 自定义面向对象数据库框架
- web前端经典面试题
- hbase region 手动 split
- linux系统负载相关的概念和度量
- Mac环境中搭建Hadoop相关问题及处理办法
- S3C2416 中不同LCD尺寸适配
- priority_queue
- 什么是抽象类
- Android adb 启动应用
- 问题:LINUX通过网站解压zip覆盖网站进行升级失败,忘记apache授权
- D3设置绝对定位与获取百分比长度的具体值
- Error occured processing XML 'Cannot find class [springmvc.extention.BeanArgumentResolver]
- JavaScript实现的XML与JSON互转功能详解
- hibernate关于多对多注解配置
- 搞懂unix、linux、ios、android的大致区别
- 剑指Offer---面试题37:两单链表中第一个公共节点