1、Xposed框架是什么?
Xposed框架是一款开源框架,其功能是可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运行。
2、Xposed模块是什么?
Xposed模块即按照Xposed模块开发规范开发出来的包含你想在别人Apk里面执行的代码的一个Apk。很多时候我们听到的微信抢红包神器、修改手机定位神器等都有可能是一个Xposed模块。
3、Xposed框架如何安装?
找一款可以root的手机或者模拟器,下载XposedInstaller.apk,安装,打开,框架->安装/更新->根据提示重启手机,安装完成
4、如何开发Xposed模块?(本文重点)
准备工作(工具、环境):
- ROOT手机,安装XposedInstaller,并安装Xposed框架
- 刷机,自带Xposed的ROM
- VirtualXposed 让你无需Root也能使用Xposed框架!
https://xposed.appkg.com/2799.html - 模拟器(夜神、Genymotion)
以上4种方式,任选,没有自带Xposed的设备需要安装XposedInstaller.apk ,推荐第四种
开发步骤:
- 创建Android 项目
- 添加依赖
// https://mvnrepository.com/artifact/de.robv.android.xposed/api
compileOnly 'de.robv.android.xposed:api:82' //注意是compileOnly,仅参与编译,不参与打包
- Manifest application节点下添加如下信息:
<application>
<meta-data
android:name="xposedmodule"
android:value="true" /> //用于标记该APK是一个Xposed模块
<meta-data
android:name="xposedminversion"
android:value="54" />//支持的最低Xposed版本,类似于Android中的minSdkVersion
<meta-data
android:name="xposeddescription"
android:value="XposedDemo 仅供学习使用" />//Xposed模块的简介,可以XposedInstall应用中看到此处设置的内容
</application>
- 首先我们来了解一些概念,hook一个类的静态属性和hook一个方法时用到的方法参数含义
/**
* hook一个类的静态属性
* @param clazz 被hook的类Class对象
* @param fieldName 被hook的静态属性名称
* @param value对该属性设置的新值
*/
public static void setStaticObjectField(Class<?> clazz, String fieldName, Object value)
XC_MethodHook hook = new XC_MethodHook() {
/**
* 在被hook的方法代码块执行前执行,相当于在方法代码块第一行之前插入这部分代码
* @param param
* @throws Throwable
*/
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//param.args被hook的方法的参数列表 param.args[0]即为该方法输入的第一个参数,
//通过这个参数我们可以修改方法的输入参数值(重要)
//param.thisObject当前被hook的类的对象
}
/**
* 在被hook的方法返回结果之前执行,相当于在return执行之前插入这部分代码
* @param param
* @throws Throwable
*/
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//param.args同上
//param.thisObject同上
//param.getResult() 获取方法的返回值
//param.setResult(object);设置方法的返回值,利用这个可以修改方法的返回结果(重要)
}
};
- 创建一个类SimpleHook实现IXposedHookLoadPackage接口,并实现handleLoadPackage方法
public class SimpleHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
}
}
- 在assets目录下创建xposed_init文件,并填入上一步创建类的完整类名(包名+类名)
com.moon.xposed.hook.SimpleHook
- 首先我们来一个简单的,先改变一下手机的型号,很简单直接修改Build类的静态变量即可
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.appInfo == null) {
return;
}
if ((lpparam.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//不hook系统APP
return;
}
XposedHelpers.setStaticObjectField(Build.class, "MODEL", "Xopsed牌");
}
- 运行安装写好的Hook APP
- 写一个简单的APP来获取设备型号
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.toast).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "这是一个Toast", Toast.LENGTH_LONG).show();
}
});
StringBuilder sb = new StringBuilder();
sb.append("型号:").append(" ").append(Build.MODEL).append("\n");
sb.append("MANUFACTURER:").append(" ").append(Build.MANUFACTURER).append("\n");
sb.append("DEVICE:").append(" ").append(Build.DEVICE).append("\n");
sb.append("BOARD:").append(" ").append(Build.BOARD).append("\n");
sb.append("BRAND:").append(" ").append(Build.BRAND).append("\n");
TextView modelTv = findViewById(R.id.model_tv);
modelTv.setText(sb.toString());
TextView manufacturerTv = findViewById(R.id.manufacturer_tv);
manufacturerTv.setText(Build.MANUFACTURER);
}
}
- 运行安装该Demo APP
- 在XposedInstall中打开“模块”界面,勾选XposedDemo模块,如图所示,并重启设备
- 打开我们的Demo APP可以看到我们修改的设备型号已经生效
到此一个最简单的Xposed模块已经制作完毕。
示例一:是谁偷偷的弹了Toast?
学以致用,大家是不是有时候会遇到Android手机不知什么原因的弹Toast,且不知道是哪个APP偷偷的在干,有了Xposed我们就可以找出这个元凶。代码很简单,直接hook这个方法即可:
public static Toast makeText(Context context, CharSequence text, int duration)
hook代码如下:
XC_MethodHook toastHook = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String text = (String) param.args[1];
text = "被Hook " + getAppName((Context) param.args[0]) + ":" + text;
param.args[1] = text;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
};
XposedHelpers.findAndHookMethod(Toast.class.getName(), lpparam.classLoader,
"makeText", Context.class, CharSequence.class, int.class, toastHook);
然后看我们的Demo APP弹Toast的相关代码:
findViewById(R.id.toast).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "这是一个Toast", Toast.LENGTH_LONG).show();
}
});
看效果:这样我们就可以知道这个Toast是一个叫test的APP弹出来的了。
示例二:实现一个小目标
曾经王首富的一句话“最好先定一个能达到的小目标,比方说我先挣它一个亿”,瞬间在网络走红,网友纷纷开始留言,有的笑谈、有的吐槽、有的自嘲……“。今天我们就来帮大家实现一个“小目标”,利用Xposed将微信钱包的余额修改为这个小目标。
- 首先我们定位到钱包余额展示页面Activity为com.tencent.mm.plugin.mall.ui.MallIndexUI,并定位到余额是在protected final void bNi()方法中,通过一个变量名为omy的TextView进行设置展示的。具体怎么定位到的属于逆向破解方法的内容,本文暂不讨论。
- 由此我们便可以通过在bNi()结束时重新设置一下omy的内容来实现我们的小目标了
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//此处为什么要在Application的attach方法结束时来hook MallIndexUI类的bNi方法呢?
//因为微信APK有分包,里面有多个dex,直接在handleLoadPackage方法里面进行hook有可能MallIndexUI类所在的dex尚未加载,就会出现找不到类的异常
ClassLoader cl = ((Context) param.args[0]).getClassLoader();
Class<?> hookclass = null;
try {
hookclass = cl.loadClass("com.tencent.mm.plugin.mall.ui.MallIndexUI");
} catch (Exception e) {
Log.e("Xposed", "查找com.tencent.mm.plugin.mall.ui.MallIndexUI报错", e);
return;
}
Log.i("Xposed", "查找com.tencent.mm.plugin.mall.ui.MallIndexUI成功 " + lpparam.processName + " " + lpparam.packageName);
XposedHelpers.findAndHookMethod(hookclass, "bNi", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//此处通过反编译得到omy属性在MallIndexUI的父类MallIndexBaseUI中声明,因此反射获取该Field需要通过父类的class
final Class base = Class.forName("com.tencent.mm.plugin.mall.ui.MallIndexBaseUI", false, lpparam.classLoader);
Field omyField = base.getDeclaredField("omy");
omyField.setAccessible(true);
//得到omy对象,直接强转为TextView进行使用
TextView omyTextView = (TextView) omyField.get(param.thisObject);
omyTextView.setText("一个亿");
}
});
}
});
- 运行Hook APP,重启设备,查看结果,一个小目标实现了
绝逼不是P图,有动画为证
其实Xposed可以做的事情很多,除了修改设备信息绕过风控做黑灰产、装逼炫酷外,还可以做一些监控,比如hook一些获取用户隐私数据的方法,用来检测是否有APP在后台恶意窃取用户隐私等。Xposed真的可以为所欲为,用在优秀的人手中就可以造福万民。
作者简介:就职于甜橙金融信息技术部,负责翼支付android客户端开发工作,喜欢研究新的技术,不断学习,不断提升。