Xposed插件开发简单介绍

Xposed介绍:

xposed是一款Android上开源的Hook框架,它可以在不改变应用程序代码的情况下改变java层代码的输出结果。

原理:

我们知道Android上应用进程都是从zygote进程fork出来的,xposed就是通过替换zygote进程,导入自己的so文件和jar包,来达到hook所有应用java层方法的目的。这里我们只是大体的了解一下,具体的内部实现原理,回头再写篇文章研究一下。

XposedInstaller

开发插件之前,我们还需要先安装XposedInstaller,这是一个APP,通过它可以方便的导入xposed框架和管理xposed模块。xposed框架只是为我们提供了一个平台,具体的hook操作就由我们开发的插件来完成。首先我们导入xposed框架,准备一个已经root过的手机或者模拟器,只有获取到了root权限,才能进行相关的操作。安装好XposedInstaller,然后点击安装xposed框架,安装完成之后如下:

已激活.png

点击左上角的按钮会弹出菜单:


菜单.png

我们点击模块按钮,就会显示当前手机上安装的所有的xposed插件信息。

模块列表.png

我手机上已经有几个插件了,在这里可以选择是否激活,然后重启手机就可以应用了。

现在就可以开发xposed的插件了。

xposed插件开发

示例APP

为了演示xposed的效果,我新建了一个TestApp工程,这个APP只有一个MainActivity:


public class MainActivity extends AppCompatActivity {

    private TextView info;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        info = findViewById(R.id.tvInfo);

        info.setText(getInfo());

    }

    private String getInfo() {
        return "hello ,my name is Tom .I am from china";
    }

}

界面长下面这个样子,仅是在屏幕上显示一行字。

sample.png

现在要做就是通过编写xposed插件,来改变屏幕上显示的这行字。

通过研究发现,屏幕上这行字的内容来自MainActivity.getInfo()方法,在xposed中,这很好办,只要把这个方法hook了,然后返回的时候做一下手脚就可以了.

开发插件

开发一个xposed插件和开发一个APP基本一样,就是通过AndroidStudio来新建一个工程,然后导入相关的依赖。

compileOnly 'de.robv.android.xposed:api:82'

注意是compileOnly,因为xposed框架已经将相关jar包注入到zygote进程中了,所以这里不需要将jar包打包进APK。

配置

新建好了工程,但是这个工程看起来平淡无奇,我们还需要进行一些配置,表明这是一个xposed插件,好让XposedInstaller认识它。

只需要在AndroidManifest中添加3个meta-data,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fengchaoqun.xposedtest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!--表明这是一个xposed插件-->
        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <!--指定xposed的最小版本-->
        <meta-data
            android:name="xposedminversion"
            android:value="30+" />
        <!--插件的描述-->
        <meta-data
            android:name="xposeddescription"
            android:value="xposed插件开发测试" />
        
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

这样,当把这个APP安装到手机,XposedInstaller就会识别出来它是一个xposed插件了,顺手把它激活一下。

sample2.png

编写hook逻辑

新建一个类实现IXposedHookLoadPackage接口:

package com.example.fengchaoqun.xposedtest;

import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookLogic implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {

        XposedBridge.log("HookLogic >> current package:" + lpparam.packageName);

    }

}

在一个应用启动的时候,会调用IXposedHookLoadPackage.handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam)方法,lpparam中有一些当前应用的信息,我只是在这里打印一下包名。

到这里,我们已经将简单的hook逻辑写好了,但是感觉还是有点不太对,因为XposedInstaller只是知道了这是一个插件,那么应该运行什么代码,XposedInstaller也是一脸懵逼。就像默认的Android工程有一个MAinActivity一样,我们要告诉XposedInstaller入口在哪。

在asset目录下新建一个名为xposed_init的文件,在这个文件里写上入口类的完整路径:

sample3.png

好了,现在可以测试一下,将这个插件安装到手机,确认在XposedInstaller中已经激活,然后重启手机。看到以下日志,说明插件生效。

log.png

在日志中我们可以看到,很多系统的进程启动了。然而现在只是打印了包名,接下来我们编写hook的代码,改变前面测试工程的输出值。

首先要知道我们需要hook的是哪个类的哪个方法,在这里,显然我们需要hook MainActivity.getInfo()
下面是修改过后的代码:

package com.example.fengchaoqun.xposedtest;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookLogic implements IXposedHookLoadPackage {

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {

        XposedBridge.log("HookLogic >> current package:" + lpparam.packageName);

        if ("com.example.fengchaoqun.testapp".equals(lpparam.packageName)) {
            try {
                XposedHelpers.findAndHookMethod("com.example.fengchaoqun.testapp.MainActivity", lpparam.classLoader, "getInfo", new XC_MethodReplacement() {
                    @Override
                    protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                        return "大家好,我是御天证道,我来自中国!";
                    }
                });
            } catch (Throwable t) {
                XposedBridge.log(t);
            }
        }

    }

}

这里出现了XposedHelpers类,这是一个工具类,它主要是对一些常用的反射操作进行了封装,例如查找某个类是否存在,查找方法,构造器,设置类内的字段值等等,以及便捷的hook操作。所以如果你想进行xposed插件开发,那么这个类你最好是完全理解,内部源码很简单。 我们这里用到XposedHelpers.findAndHookMethod方法,这个方法是用来hook指定类的指定方法的,第一个参数是完整的类名,第二个参数是classloadr,这里也就是当前应用的classloadr,第三个参数是方法的名字,最后一个参数是一个数组,这个数组要求前面是方法的入参类型,例如int就写int.class,没有入参则不写,但是要求数组最后一位的值是一个XC_MethodHook回调,我们就是通过这个回调来获取方法的实际入参,修改返回值的。我这里传入的是XC_MethodReplacement,它实现了XC_MethodHook,用replaceHookedMethod中返回的值替换了被hook的方法的返回值。

接下来我们重新安装插件、重启手机,然后打开测试APP:

result.png

可以看到,我们在没有修改原来APP代码的情况下,改变了方法的返回值。

这只是一个xposed插件开发的简单介绍,当然实际开发中肯定不是这么简单的,光是找到需要hook的方法可能就需要花费一定的时间。后面有时间我会详细介绍xposed的工作原理、开发中遇到的坑以及免重启方案(这个是提升开发效率的重中之重)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容