那个能让你为所欲为的Xposed你需要了解一下

2022-12-22  本文已影响0人  常怀德

前言

最近在处理微信公众号的数据采集,需要逆向获取微信你公众号的消息推送,接触到了传说中的逆向神器Xposed。据说可以用它为所欲为,哈哈哈,做网络安全的不会让你得逞。但是虽然做不到为所欲为,至少可以在网络安全师的草场上打马悠行一遭,顺便捡捡数据金矿。话不多说,本文先带你了解一下什么是Xposed。
本文主要参考崔庆才文章:https://blog.csdn.net/cqcre/article/details/106561367

Xposed Framework介绍

Xposed 框架是一款可以在不修改 APK 的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。Xposed不是个新鲜工具,早年就有各种应用场景,比如刷系统,获取root权限,更改UI,开发插件……在这些场景的催化下逐渐形成了Xposed生态,目前已经有相当多种类的插件被开发出来,这也归功于开源,尤其是Android的开源。
使用xposed需要有一定的Android基础,基本上需要有读懂源码的能力,xposed本身并不复杂,复杂点在于反编译后从源码中获取到一个口子,然后插根管子进去(hook),要么是改变数据变量,要么是截取数据。所以玩Xposed有两个关键点,一个是找那个口子,另外一个是插根管子。

Xposed原理

Android基于Linux,第一个启动的进程自然是init进程,该进程会启动所有Android进程的父进程——Zygote(孵化)进程,该进程的启动配置在/init.rc脚本中,而Zygote进程对应的执行文件是/system/bin/app_process,该文件完成类库的加载以及一些函数的调用工作。在Zygote进程创建后,再fork出SystemServer进程和其他进程。

而Xposed Framework呢,就是用自己实现的app_process替换掉了系统原本提供的app_process,加载一个额外的jar包,然后入口从原来的:com.android.internal.osZygoteInit.main()被替换成了:de.robv.android.xposed.XposedBridge.main(),然后创建的Zygote进程就变成Hook的Zygote进程了,而后面Fork出来的进程也是被Hook过的。这个Jar包在:
/data/data/de.rbov.android.xposed.installer/bin/XposedBridge.jar

这里从另外一个地方找到了Xposed原理讲解比较通俗:
https://sspai.com/post/40121
Xposed 是一个 Android 平台上的动态劫持框架,通过替换手机上的孵化器 zygote 进程为 Xposed 自带的 zygote,使其在启动过程中加载 XposedBridge.jar,模块开发者可以通过 jar 提供的 API 来实现对所有的 Function 的劫持,在原 Function 执行的前后加上自定义代码。

说成人话就是 Android 上所有正在运行的应用程序都是通过一个万物之主 —— Zygote 创造出来的,但是这个万物之主不听我们的话呀,Xposed 就是把这个万物之主换成自个家的,然后用一本天书(就是前面的 XposedBridge.jar)和他沟通,就可以实现对于系统上任意程序任意数据和行为运行前后的修改,至于我们不会写天书或者读懂天书也没关系,只要我们下载已经写好的天书(模块)就可以完成特定的需求。


xposed原理

举个实际的例子,在某个 App 的界面被系统渲染出来之前,Hook(勾住)负责渲染界面的函数,把 App 传递过来的参数(例如红色)改成绿色,最后 App 界面就可以成功变成你想要的颜色啦(当然是选择原谅她)。


xposed修改

Xposed环境说明

Xposed需要最高权限即Root权限,因此在不明源码的情况下尽量不要使用别人写出来的代码,特别注意不要在生产环境或者自己常用手机系统上搞,容易被人当枪使还泄露自己隐私。

相关文档

环境

首先需要在电脑端安装好Android环境,另外需要root环境,可以用真机刷个root环境,也可以用Android模拟器,因为它自带root环境。
Android环境配置请参考以下的教程:
https://juejin.im/post/5d255101e51d4556d86c7b4f
https://juejin.im/post/5d5eb3ed5188252ae10be138

本次暂且在网易Android模拟器mumu上操作吧。

安装Xposed Installer

要安装 Xposed 我们需要借助于一个 App,叫做 Xposed Installer,它就是用来安装 Xposed 框架的,利用它我们可以下载和安装 Xposed 框架,同时还能查看和管理 Xposed 模块,还能查看一些 Xposed 框架输出等日志信息等。

在官网上可以下载Xposed Installer:
https://repo.xposed.info/module/de.robv.android.xposed.installer

其中明确指定5.0以上Android系统的下载


下载Xposed Installer

下载链接:https://forum.xda-developers.com/attachment.php?attachmentid=4393082&d=1516301692

安装完Xposed Installer后需要安装Xposed,下载更新并激活后重启设备后即可使用


激活Xposed框架

Xposed开发

其实本质上来说,它就是一个安卓 App,开发一个安卓模块其实流程上就和开发一个安卓 App 差不多,只不过相比 App 开发来说多了下面四个步骤:

就这么四步,这四步这么来实现呢,下面我们来一步步实现。

首先在Android studio上创建一个 Empyty Activity项目。


创建项目

接下来给项目起一个名称,指定项目存储位置。


项目命名

创建完后会自动生成一个MainActivity


MainActivity

第一步就是要做一些标识符,让Xposed框架认识这个模块。
我们打开 AndroidManifest.xml 文件,添加如下内容:

<meta-data
  android:name="xposedmodule"
  android:value="true" />
<meta-data
  android:name="xposeddescription"
  android:value="Xposed Test" />
<meta-data
  android:name="xposedminversion"
  android:value="53" />

到 application 标签内,和 activity 标签并列,最终内容如图所示:


image.png

这里指定了三个 meta-data,分别为:

xposedmodule:这里设置为 true,代表这是一个 Xposed 模块。
xposeddescription:模块的描述,填写模块描述就好,就是一个字符串。•xposedminversion:模块运行要求的 Xposed 最低版本号,这里是 53。

定义好这三个内容之后,把这个 App 安装到手机,Xposed 就能识别出这个 App 是一个 Xposed 模块了。

我们点击运行按钮,在手机上运行这个 App。
在模拟器上会有如下显示:


image.png

此时xposed模板里面也有了XposedActivity模块


image.png

接下来我们再在项目中引入 Xposed 相关的 SDK,这样我们才能调用 Xposed 提供的一些 Hook 操作方法,实现 Hook 操作。

打开 app/build.gradle 文件,在 dependencies 区域添加如下两行代码:

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

这是 Xposed 的 SDK,添加之后 Android Stuido 会检测到项目配置发生的变化,在右上角会提示一个 「Sync Now」的选项,我们点击之后,新添加的 Xposed SDK 便会自动下载和安装,如图所示:


image.png

好,现在 Xposed 的 SDK 就安装成功了,下面我们就能使用里面的方法实现逻辑的 Hook 了。
那怎么来实现逻辑的 Hook 呢?Hook 什么呢?那总得有点逻辑吧?哪来的逻辑呢?自己先写一个吧。

说干就干,这里我们就加一个鼠标响应事件,点击之后触发一个算式计算的逻辑吧。

首先我们修改下页面内容,把当前的文本框设置成一个按钮,以便点击触发,修改 app/src/main/res/layout/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">
    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="click me"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

接下来再修改下 MainActivity.java 文件,内容如下:

package com.example.xposedactivity;


import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, showMessage(1, 2), Toast.LENGTH_SHORT).show();
            }
        });
    }

    public String showMessage(int x, int y) {
        return "x + y = " + (x + y);
    }
}

这里我们定义了一个 Button,然后使用 findViewById 方法从视图里面获取到了这个 Button 对象,同时我们为这个 Button 添加了一个点击事件,点击之后会生成一个 Toast 提示,其内容为 showMessage 方法返回的结果。

这个 showMessage 方法接收两个参数,是 int 类型的 x 和 y,返回的结果是一个字符串,即「x + y = 」这个字符串再拼接上二者计算得到的结果,其实就是一个算数表达式。

这里 showMessage 在调用的时候我们传入了 1 + 2,所以最后 showMessage 显示的结果应该为 「x + y = 3」,我们重新运行下 App,然后点击 TEST 按钮,可以看到如下运行结果:

image.png

定义好了基本程序之后呢,下一步我们就来用 Xposed 进行 Hook 吧,我们在 MainActivity.java 同级新建一个 Java Class,文件名一定要和Class名一样,此处就因为没有命名成同样的找了半天问题,浪费时间内容如下:

package com.example.xposedactivity;

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;

public class HookMessage implements IXposedHookLoadPackage {

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

        if (loadPackageParam.packageName.equals("com.example.xposedactivity")) {
            XposedBridge.log("Hooked com.example.xposedactivity Package");
            Class clazz = loadPackageParam.classLoader.loadClass(
                    "com.example.xposedactivity.MainActivity");
            XposedHelpers.findAndHookMethod(clazz, "showMessage", int.class, int.class, new XC_MethodHook() {
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log("Called beforeHookedMethod");
                    param.args[0] = 2;
                    XposedBridge.log("Changed args 0 to " + param.args[0]);
                }

                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log("Called afterHookedMethod");
                }
            });
        }
    }

}

这里我们就定义了 Hook 的相关逻辑了,这里梳理几个关键的点:

这里我们先对 beforeHookedMethod 处理,对 param 的 args 属性做了处理,这里的 args 属性是一个列表,就是 showMessage 方法的调用参数,因为我们之前传入的是 1 和 2,所以这里 args 属性的值其实就是 [1, 2],那这里我们是把它改写了一下,把第一个内容改写成了 2,那这里 args 其实就会变成 [2, 2] 了

好,现在 Hook 的逻辑我们已经实现好了,还差最后一步,那就是告诉 Xposed 我们的 Hook 逻辑是定义在哪里了,我们需要新建一个 Xposed 入口文件。

在 main 文件夹新建一个 assets folder,如图所示:


批注 2020-08-13 134748.png

然后在 assets 文件夹下新建一个 xposed_init 文件,不需要有任何后缀。内容就把 Hook 的这个类的路径写好就好了,内容如下:

com.example.xposedactivity.HookMessage

这样保存之后,Xposed 就能自动读取这个 xposed_init 文件来执行我们自定义的 Hook 逻辑了。

最后,我们就来重新运行看下效果吧。

记得安装完成之后重启一下 Xposed,否则是没有效果的。

重启模块之后,点击 click me 按钮,可以看到就出现了如下效果,如图所示:


image.png

这里可以看到,最后的运行效果就不一样了,出现了「x + y = 4」的这个现象,这说明通过 beforeHookedMethod 的定义,我们成功把 args 的第一个参数,也就是 x 修改成了 2,而第二个参数没有修改,还是 2,最后就相当于 showMessage 调用之前,两个参数就被修改成了 2 和 2,最后答案就是 4 了。

这下我们就体会到了 beforeHookedMethod 的用法了。

刚才我们是用了 beforeHookedMethod 来实现了参数替换的效果,接下来我们再来体会一下 afterHookedMethod 的用法,它可以对方法的返回结果进行后处理,比如这里我们把 afterHookedMethod 修改为如下内容:

protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    XposedBridge.log("Called afterHookedMethod");
    param.setResult("Hooked");
}

这里我们增加了 param 的 setResult 方法的调用,利用它我们可以直接将方法的返回值修改掉。

重新运行这个模块,然后重启手机,同样地还是点击 click me按钮,这时候我们发现其结果就变成了如下内容,如图所示:


image.png

可以看到最后方法的返回结果被修改了,这正是 afterHookedMethod 所起的作用。

由此,我们通过 beforeHookedMethod 和 afterHookedMethod 的修改可以实现 showMessage 在调用前和调用后的修改。

好,最后我们再来看下日志,打开 Xposed Installer 的日志页面,可以看到内容如图所示:


image.png

可以看到这里就输出了我们用 XposedBridge 的 log 方法输出的内容。

到此为止我们就实现了 Xposed 的 Hook 逻辑了,通过这个案例大家应该就能体会到 Xposed 的效用了。

上一篇下一篇

猜你喜欢

热点阅读