编写一个简单的xposed模块
文章最后更新时间为:2021年08月08日 22:58:56
xposed和magisk作为刷机佬熟悉的东西,就不多说了,本文主要是记录一下xposed模块怎么编写,比较简单,大神略过。
(这里的xposed也就是EdXposed:https://github.com/ElderDrivers/EdXposed)
1. 编写一个demo模块
编写一个xposed模块,也就是开发一个安卓app。和普通程序本质上是一样的,不一样的点在于
- 让EdXposed知道我们安装的这个程序是个xposed模块。
- 模块里要包含有xposed的API的jar包,以实现下一步的hook操作。
- 这个模块里面要有对目标程序进行hook操作的方法。
- 要让手机上的xposed框架知道,我们编写的xposed模块中,哪一个方法是实现hook操作的,也就是hook类的入口。
1.android studio创建一个新的空项,package为com.xposed.demo
2.添加Xposed Framework API到project,在app/buid.gradle中的dependencies段里添加:
dependencies {
...
// 引入xposed文档和源码
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
}
3.修改AndroidManifest.xml,告诉EdXposed知道我们安装的这个程序是个xposed模块。做法是在application字段中加入
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="这是一个xposed demo" />
<meta-data
android:name="xposedminversion"
android:value="53" />
- xposedmodule 表明是一个xposed模块
- xposeddescription 描述
- xposedminversion API version(一般等于构建它的Xposed的version)
4.编写hook代码,在MainActivity同级目录建立HookDemo.java:
package com.xposed.demo;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class HookDemo implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Loaded app: " + lpparam.packageName);
}
}
所有的hook入口类必须是IXposedMod的实现类,这个例子里,我们记录下被加载app的名字,所以实现了IXposedHookLoadPackage,它只有一个方法带有一个参数,这个参数带有上下文信息。
这个log方法会输出到logcat。
5.告诉XposedBridge hook入口在哪里,在app/src/main目录下创建assets文件夹,在assets文件夹内创建文件xposed_init, 该文件每行包含一个class的全限定名,在这里文件内容为:
com.xposed.demo.HookDemo
6.编译运行,在手机上安装并启用这个xposed模块。
- 重启手机,使模块生效
- 监听log
adb logcat | grep "Loaded app:"
可以看到所有的应用在启动的时候,都被我们记录了日志。
2. hook app 实战
既然学会了xposed模块的基本用法,下面继续来hook app实战。
首先编写一个apk,界面上只有一个button,实现一个弹窗。
- MainActivity.java
package com.saucerman.demo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public String toastMessage() {
return "这是一条信息?";
}
}
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="1dp"
android:layout_marginBottom="90dp"
android:text="Button"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
app运行时的界面如下,点击button,弹窗内容为这是一条信息
:
接下来我们来编写xposed模块,实现对这个弹窗内容的修改。遵循第一章节的方式,我们创建一个xposed模块,修改模块的HookDemo类,来实现hook toastMessage函数的返回值:
package com.xposed.demo;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class HookDemo implements IXposedHookLoadPackage {
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("com.saucerman.demo")) {
Class clazz = lpparam.classLoader.loadClass(
"com.saucerman.demo.MainActivity");
XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult("你已被劫持");
}
});
}
}
}
然后安装这个xposed模块,并且重启我们写好的弹窗app,再次运行:
可以看到弹窗内容被成功修改了,上面的HookDemo代码中,判断了如果程序的package为com.saucerman.demo,则进行hook。将toastMessage函数的result设置成了"你已被劫持"。其中
1.findAndHookMethod是一个工具方法,使用方式为findAndHookMethod(目标class名,lpparam.classLoader,方法名,参数1,参数2....,new XC_MethodHook())
- 如果要hook的方法有多个参数,还需要列出参数的class类型
- 最后一个参数,需要提供一个XC_MethodHook的实现类
2.XC_MethodHook有两个method,beforeHookedMethod和afterHookedMethod。根据名字很容易知道这两个method在被hook的method前后执行。
- beforeHookedMethod 主要用来读取和修改参数
- afterHookedMethod 主要用来修改返回值以及查看修改是否生效