目录

Xposed初体验

Xposed

对于Xposed这个框架,在很早之前就已经了解到了,那时候是用它来实现自动抢红包这样的功能,总之是一个非常厉害的框架。不过这些年似乎热度正逐渐被Magisk夺取,不过因为两者实现方式不同,所以在不同的场景下还是各有各的存在意义。比如说Magisk使用起来风险更低,因为它并没有真正修改系统文件,也就不会轻易出现系统崩溃,卡开机屏的问题。不过要说插件数量,据我所知还是老牌的Xposed比较多。

Xposed是通过劫持安卓系统最初始的进程zygote实现工作的,任意一个进程总是通过fork zygote产生的,所以,Xposed劫持了它之后,就能对其他进程做任何事情了。

因为目前并不打算root手中的主力机,所以打算使用一部不用的手机金立s5来作为试验机,不过在我辛苦的root它之后,发现想要安装Xposed还真是麻烦。并且我也不想维护两个手机,并且我知道手机上会有虚拟机,它一般都会自带Xposed框架。由此,觉得Xposed会更方便。说起来,如果在主力机上面实现了一些实用的功能,使用起来也会很方便。

我也尝试过安装Magisk,但是,并没有成功,在通过fastboot刷写boot的时候电脑一直找不到设备,不清楚是驱动问题,还是手机限制的问题,而且这部手机网上能找到的资料很少,所以不方便折腾。于是就完全放弃了使用s5作为试验机的想法。

环境搭建

目前手持主力机红米K30U,联发科的SOC使得这个手机像是后娘养的一样,啥东西都赶不上热乎的,希望未来随着滥发可机型变多小米能够对其有更多的优化。不过好在并不影响我的使用。

现在的设想是通过安装VMOS这个虚拟机软件在手机上面运行一个系统,免费版本的是Android 5.1.1,里面带了ROOT还有Xposed并且能够很方便的打开网络adb方便使用笔记本对其操作,实际上这也是安装软件和传输文件最方便的方式了,尤其是能够和Android Studio无缝协作。

配置开发环境

安装Android Studio (AS)

下载链接在此https://developer.android.google.cn/studio/

此时有需要说明的是,网络上讲关于Xposed模块开发的教程都会提到关闭ASInstant Run,但是我下载最新版本之后,在设置中并没有找到这个的设置项。让我困惑了很久,直到无意间了解到这个功能在3.5版本的时候就被谷歌"优化"掉了。被新的Alpply Change功能替代了。但是不清楚这个新的功能是否会导致出现教程中说到的异常,我是用的是Android Studio 3.2.1好在官网能够下载到。

安装之后,国内可能需要设置代理才能够愉快的下载SDK以及相关jar依赖库,毕竟是谷歌的服务,被墙了也是毫不意外的事情。

之后,需要下载SDK,建议下载7.1.1 (API 25)5.1.1 (API 22),这两个是最多的版本了,另外,对于Android Studio 3.2.1来说,SDK 10 (API 29)似乎是必须安装的,对于Android Studio 4.2来说,必须安装SDK 11 (API 30),总共下载大小似乎还没有超过一个G。所以梯子流量不多的人也不用担心。

创建项目

再然后,创建一个项目,可以是没有MainActivity的项目,不过我选择了一个Empty MainActivity的项目,目的是为了好看 : P (实际上是为了方便观察apk是否真的安装成功了)

添加新的

需要在项目的AndroidManifest.xml中添加 (具体参看网上很多的教程,这部分是没有问题的)

<meta-data
    android:name="xposedmodule"
    android:value="true" />
<meta-data
    android:name="xposeddescription"
    android:value="我是一个Xposed例程" />
<meta-data
    android:name="xposedminversion"
    android:value="53" />

添加这三个条目的原因是为了能让Xposed Manager认识开发的模块。此时编译安装之后,Xposeed Manger就能看到这个模块了

https://qiniusave.xint.top/img/image-20210606141722656.png

需要说明的是,如果一开始创建项目的时候没有Activity,格式可能和我图片中的不一样,所以这也是我创建了一个带Activity的项目

导入模块所依赖的jar

需要添加以下内容

repositories {
    jcenter()
}
provided  'de.robv.android.xposed:api:82'
provided  'de.robv.android.xposed:api:82:sources'

https://qiniusave.xint.top/img/image-20210606141342040.png

添加完毕之后,Android studio会提示你Sync,点击之后,就会自动从仓库下载对应的jar到项目中,很是方便。(但愿你开了代理)

provided 的作用是 在打包apk时不包含依赖包

一开始自作聪明的认为82指的是版本号,我安装的是89版本,所以修改成了89结果失败了,真是自作聪明

Xposed模块开发教程(二) 第一个Xposed模块应用-在手机状态栏增加显示cpu温度_没错, 我是东芝-CSDN博客_com.android.systemui.statusbar.policy.clock

创建用于hook的代码

新建代码文件,此处我在入口类同级目录创建了一个类文件,取名HookTest,它是继承于IXposedHookLoadPackage接口的,这部分代码和可以从网上教程中复制(指导入的库名称以及IXposedHookLoadPackage这个很长的接口名称)

https://qiniusave.xint.top/img/image-20210606141916216.png

上图的最终代码我会提供到最后,并且做出解释

开始编写代码

对于此次任务,依旧是获取Shaft这个软件的图片链接,对于这部分内容直接参看之前的文章

使用Frida完成android的hook - 星途 · 小镇 (lonelysinging.github.io)

通过反编译,得到我们需要hook的方法,当然,如果有源码当然是最好的了,不过如果是这样的话,直接修改源码岂不是更快?

现在假设你根据上面的文章得到了需要hook的方法

代码

那么我的代码是下面这样的

package com.example.startu.myapplication233;

import android.os.Environment;

import java.io.File;
import java.io.FileOutputStream;

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 HookTest implements IXposedHookLoadPackage {

    public void writefile(String str) throws Throwable{
        String sdCardDir =Environment.getExternalStorageDirectory().getAbsolutePath();
        File saveFile = new File(sdCardDir, "aaaa.txt");
        FileOutputStream outStream = new FileOutputStream(saveFile,true);
        outStream.write((str+"\n").getBytes());
        outStream.close();
    }

    public void handleLoadPackage(final LoadPackageParam loadPackageParam) throws Throwable {
        XposedBridge.log("Loaded app: " + loadPackageParam.packageName);
        writefile("启动包: "+loadPackageParam.packageName);
        if (loadPackageParam.packageName.equals("ceui.lisa.pixiv")) {
            writefile("发现目标: "+loadPackageParam.packageName);
            Class<?> clazz = XposedHelpers.findClass("ceui.lisa.models.ImageUrlsBean", loadPackageParam.classLoader);
            XposedHelpers.findAndHookMethod(clazz, "getMedium", new XC_MethodHook() {
                protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                }

                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    // param.setResult("你已被劫持");
                    writefile("新的图片:");
                    writefile((String)param.getResult()+"\n");
                }
            });
        }
    }
}

解释

作为Xposed的模块,当然是以回调的方式被调用。而需要编写的回调函数就是handleLoadPackage函数,这个函数将会在每次系统创建进程的时候被调用。通过if (loadPackageParam.packageName.equals("ceui.lisa.pixiv"))过找到需要hook的进程,接着找到类和方法。实际上如果会Java的话,对于上面的代码应该一眼就看懂了。唯一需要补充的是,beforeHookedMethodafterHookedMethod这两个方法,他们分别在执行原函数之前和执行原函数之后被调用,前者你可以修改其参数,后者可以修改其返回值。这样会让原函数很没有尊严。。。

另外需要解释,我为什么会写一个writefile函数,为什么不用框架自带的log系统。原因是因为我发现在手机安装的虚拟机中,Xposed的日志系统会失效,显示不出来日志。我曾一度以为模块没有正常工作,为此我换了各种虚拟机,各种版本。折腾了一下午,最后想到可能是日志系统出问题了?最后抱着试一试的心态写了一个writefile函数,没想到工作正常,当时就气哭了 (

实际上,框架在夜神模拟器 (Android 5版本)上工作完全正常,不过它的Android 7版本并没有自带Xposed框架,应用商场的是Android 5的,如果安装了会导致卡开机屏幕

指定入口类

Xposed模块在会从入口配置xposed_init文件里找入口类,在assets创建一个xposed_init文件,如果没有assets文件夹,则自行创建

https://qiniusave.xint.top/img/image-20210606145459689.png

其内容是上面编写的Hook

结果

https://qiniusave.xint.top/img/image-20210606150735724.png

储存目录下能看到命名为aaaa.txt的文件,其中有所有启动的进程,以及Shaft得到的所有图片的链接

通过XposedFrida都能实现这样的功能,但是也能看出他们的区别,一般来说Frida需要电脑配合(通过手机安装Termux之类的软件应该可以免电脑),但是Xposed则不需要,而且适合长期修改。Frida方便用于解密,或者用于破解认证一类不需要长久hook的需要。

参考 无敌的网友

Xposed模块开发教程整理 - leigq - 博客园 (cnblogs.com)

【Xposed模块开发入门】真·第一课 - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn