Android Xposed 框架

一、Xposed框架简介

  • Xposed是Android平台上的一个常用的HOOK框架,可以在不改变程序源代码的前提下,影响程序的运行。一个支持Xposed的Android应用程序被称为一个Xposed模块,用户可以在Xposed中安装各种各样的Xposed模块,在启用或关闭Xposed模块之后,需要重新引导系统才能生效。
  • Xposed模块使用Java语言开发,这意味着开发人员可以使用Android Studio开发一个自己的Xposed模块,就像开发一个Android应用程序一样简单。

二、Xposed框架安装

  • 安装Xposed框架的前提是手机要有root权限,或者有第三方Recovery
  • 在Android手机上安装Xposed是一件有风险的事,在尝试之前需要对手机的Recovery、root等知识有一定的了解,否则请不要贸然尝试。同时不建议在日常使用的手机上尝试安装Xposed框架,因为一些应用程序可以检测它,并且以此为理由来封禁您的帐号(例如微信)。
  • Xposed有两种安装方法,第一种是通过Xposed Installer来安装,首先安装上Xposed Installer,它会自动检测手机的Android版本,同时可以下载Xposed来执行安装,安装时候可以直接进行安装(可能需要root权限)也可以通过Recovery来安装。第二种方式就是直接下载Recovery的包来安装,使用Recovery进行刷入。
  • 以上资源在XDA论坛上都有提供,下载地址:[OFFICIAL] Xposed for Lollipop/Marshmallow/Nougat/Oreo [v90-beta3, 2018/01/29]

三、Xposed框架开发环境搭建

  • 上文已经提到,开发一个Xposed模块并不困难,因为Xposed模块也是使用Java语言开发的,这里使用Android Studio来开发一个Xposed模块。关于Android应用开发的基本知识,这里不再赘述,读者如果不了解Android开发,可以参考Android Developers上的入门指南
  • 在创建好工程之后,首先需要配置Gradle,以添加Xposed依赖库,在app文件夹下的build.gradle文件中,将以下语句添加到dependencies中:
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
  • 在旧版本的Gradle中,上面的语句应该替换为:
provided 'de.robv.android.xposed:api:82'
provided 'de.robv.android.xposed:api:82:sources'
  • 这样就配置好了Xposed的依赖库,下面我们需要在AndroidManifest.xml中,声明本程序是一个Xposed模块,在<application></application>中添加以下XML语句:
<meta-data
    android:name="xposedmodule"
    android:value="true"/>
<meta-data
   android:name="xposeddescription"
   android:value="My Xposed Module"/>
<meta-data
   android:name="xposedminversion"
   android:value="54"/>
  • 上面三个meta-data指定了Xposed模块标识、Xposed模块描述(显示在Xposed Installer中的模块库中)以及最低Xposed版本。
  • 最后,我们在工程的assets文件夹下新建一个文本文件,命名为xposed_init,这个文件用于配置Xposed的入口类名,在文件中直接写入实现了IXposedHookLoadPackage接口的类名即可,本类中的实例为:
cn.xiaolus.xposed_xiaolu.XposedMain
  • 经过上述配置,Xposed开发环境就已经准备完毕,可以开始编写Xposed模块了。

四、Xposed API使用

  • Xposed最常用的功能就是修改函数的行为,例如返回值,这里也只阐述这部分API的使用。首先我们新建一个Java类,命名为XposedMain,位于包cn.xiaolus.xposed_xiaolu当中,这和xposed_init中的配置一致,该类需要实现IXposedHookLoadPackage接口。在该接口中,提供了一个名为handleLoadPackage的方法,这个方法在Android系统每次加载一个包的时候都会被调用。
public class XposedMain implements IXposedHookLoadPackage {
    /**
     * 加载每个包时的回调方法
     * @param loadPackageParam 加载的Package的参数
     * @throws Throwable
     */
    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
        //Do something
    }
}
  • 如前所属,因为在每个包加载的时候这个方法都会被调用,所以请勿在此方法中编写耗时的操作,否则整个系统都可能会卡死。一般而言我们只需要关心我们目标程序所在的包即可,通过以下的方法可以取出加载的包名:
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
    //判断当前加载的包名
    if(loadPackageParam.packageName.equals("cn.xiaolus.syscheck")) {
        //Do something
    }
}
  • 对于我们关心的包来说,我们还需要找到对应的类和方法,这样才可以修改其运行的行为,通过以下代码可以实现我们的目标:
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
    // 判断当前加载的包名
    if(loadPackageParam.packageName.equals("cn.xiaolus.syscheck")) {
        //
        XposedHelpers.findAndHookMethod("cn.xiaolus.syscheck.SecCheck",
                    loadPackageParam.classLoader,
                    "checkRoot", new XC_MethodHook() {
                        /**
                         * 在Hook指定的方法之前调用此回调方法
                         * @param param 方法Hook参数
                         * @throws Throwable
                         */
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            super.beforeHookedMethod(param);
                            param.setResult(false);
                        }
                        /**
                         * 在Hook指定的方法之后调用此回调方法
                         * @param param 方法Hook参数
                         * @throws Throwable
                         */
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws  Throwable {
                            super.afterHookedMethod(param);
                            param.setResult(false);
                        }
                    });
    }
}
  • 上面的代码使用了XposedHelpersfindAndHookMethod方法,需要四个参数或者五个参数,四个参数的用于HOOK无参数的方法,参数分别为完整类名、ClassLoader对象、方法名以及一个回调接口;五个参数的话在方法名之后需要提供Class[]形式的参数类型表,其他参数相同。
  • XC_MethodHook接口有两个回调方法,一个是beforeHookedMethod,一个是afterHookedMethod,分别是在HOOK指定的函数之前调用和之后调用,具体使用哪个回调方法可以自己尝试。在这两个方法中可以通过MethodHookParam参数获得并修改函数的各种参数,例如通过setResult方法修改函数的返回值,就实现了对函数方法返回值的修改。
  • 运行程序,目标函数无论执行情况如何,返回值都为false,这一般用于一些权限或者框架检测的破解和绕过,也可以用于其他的用途。

五、总结

  • Xposed是一款优秀的Android HOOK框架,优势是模块编写起来相对比较容易,功能强大,缺点也比较明显,每次重新编译框架或者开启、关闭框架,都需要重新引导系统,比较麻烦,同时不能直接HOOK C/C++编写的函数,也比较容易被检测出来(当然了你可以把检测代码也HOOK掉),也是其局限性之一。
  • 关于Xposed API的使用,更多的细节当然是参考官方的文档。有时间考虑研究一下另一款HOOK框架Frida的使用。