编写一个简单的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

2021-08-04T11:15:27.png

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)

2021-08-04T11:31:31.png

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

2021-08-04T12:30:23.png

6.编译运行,在手机上安装并启用这个xposed模块。

2021-08-04T11:49:59.png

  1. 重启手机,使模块生效
  2. 监听log
adb logcat | grep "Loaded app:"

2021-08-04T12:32:06.png

可以看到所有的应用在启动的时候,都被我们记录了日志。

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,弹窗内容为这是一条信息

2021-08-08T06:07:00.png

接下来我们来编写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,再次运行:

2021-08-08T06:09:13.png

可以看到弹窗内容被成功修改了,上面的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 主要用来修改返回值以及查看修改是否生效

参考

1 + 5 =
快来做第一个评论的人吧~