Xposed Api详解到RPC的使用

admin 2024年10月23日22:04:23评论58 views字数 15393阅读51分18秒阅读模式
VOL 154

Xposed Api详解到RPC的使用

30

2023-6

今天距2024年184天

这是鸣谦安全第154次推文

点击上方蓝字“鸣谦安全“关注我,周二,周五,每晚 19:00准时推送。

微信公众号后台回复“资源”领取学习资料,回复“QQ群”一起进群学习闲聊。

本文4529字,阅读约需10分钟

文章首发于奇安信攻防社区:https://forum.butian.net/share/2276

在日常移动安全工作中,一定会使用 hook ,那使用 hook 一定离不开一款安全工具,那就是 Xposed 。

关于 Xposed 的使用,官网列举出上百条的 Api,这样很不利于查找和使用。本文对日常工作中常用的 Xposed Api 进行详解并演示使用方法。最后结合 NanoHttpd 弥补了 Xposed 不能 RPC 的功能。                                   


XposedApi详解

要想学习 Xposed Api 一定要借助官方文档。

官方 Api 文档网址:https://api.xposed.info/reference/packages.html

下面介绍在开发 Xposed 插件中最常使用的几种方式


在介绍之前,要先创建一个测试 App

测试 App 共两个类,一个是 MainActivity 类,一个是 Dog 类。

MainActivity 类代码:

 1package com.bmstd.hookdog;
2
3import androidx.appcompat.app.AppCompatActivity;
4
5import android.os.Bundle;
6import android.view.View;
7import android.widget.Button;
8import android.widget.Toast;
9
10public class MainActivity extends AppCompatActivity implements View.OnClickListener {
11    Button bt_hookNormalMethod;
12    Button bt_hookConstructMethod;
13    Button bt_hookEat;
14    Button bt_hookOverloadEat;
15    Button bt_hookInner;
16    Dog dog;
17
18    @Override
19    protected void onCreate(Bundle savedInstanceState) {
20        super.onCreate(savedInstanceState);
21        setContentView(R.layout.activity_main);
22        bt_hookNormalMethod = findViewById(R.id.bt_hookNormalMethod);
23        bt_hookConstructMethod = findViewById(R.id.bt_hookConstructMethod);
24        bt_hookEat = findViewById(R.id.bt_hookEat);
25        bt_hookOverloadEat = findViewById(R.id.bt_hookOverloadEat);
26        bt_hookInner = findViewById(R.id.bt_hookInner);
27
28        bt_hookNormalMethod.setOnClickListener(this);
29        bt_hookConstructMethod.setOnClickListener(this);
30        bt_hookEat.setOnClickListener(this);
31        bt_hookOverloadEat.setOnClickListener(this);
32
33        bt_hookInner.setOnClickListener(new View.OnClickListener() {
34            @Override
35            public void onClick(View v) {
36                Toast.makeText(MainActivity.this"内部类被调用", Toast.LENGTH_LONG).show();
37            }
38        });
39
40    }
41
42    @Override
43    public void onClick(View v) {
44        switch (v.getId()) {
45            case R.id.bt_hookNormalMethod:
46                dog = new Dog("辛巴"14);
47                int countSum = dog.count(100200);
48                Toast.makeText(this"得到的结果是:" + countSum, Toast.LENGTH_LONG).show();
49                break;
50            case R.id.bt_hookConstructMethod:
51                dog = new Dog("靓仔"15);
52                String dogString = dog.toString();
53                Toast.makeText(this, dogString, Toast.LENGTH_LONG).show();
54                break;
55            case R.id.bt_hookEat:
56                dog = new Dog("公爵"13);
57                String eatResult = dog.eat("猪肉");
58                Toast.makeText(this, eatResult, Toast.LENGTH_LONG).show();
59                break;
60            case R.id.bt_hookOverloadEat:
61                dog = new Dog("哈利"11);
62                String eatOverLoadResult = dog.eat("狗粮"50);
63                Toast.makeText(this, eatOverLoadResult, Toast.LENGTH_LONG).show();
64                break;
65        }
66    }
67}

Dog 类代码:

 1package com.bmstd.hookdog;
2
3class Dog {
4    String name;
5    private int age;
6    static String type = "狗类";
7
8    public Dog(String name, int age) {
9        this.name = name;
10        this.age = age;
11    }
12
13    public static String work(String w) {
14        return w;
15    }
16
17    public static String work(String w, int h) {
18        String str = "狗正在" + w + "已工作" + h + "小时";
19        return str;
20    }
21
22    int count(int num1, int num2) {
23        int sum = 0;
24        sum = num1 + num2;
25        return sum;
26    }
27
28    public String eat(String foodName) {
29        return "我爱吃" + foodName;
30    }
31
32    public String eat(String foodName, int amount) {
33        return "我爱吃" + foodName + ",一次吃" + amount + "克";
34    }
35
36    private int sub(int num) {
37        int subResult = num - 1;
38        return subResult;
39    }
40
41    @Override
42    public String toString() {
43        return "Dog{" +
44                "name='" + name + ''' +
45                ", age=" + age + ''' +
46                ", type=" + type + ''' +
47                '}';
48    }
49}

hook 构造方法

hook 构造方式使用 api,XposedHelpers.findAndHookConstructor。

hook 构造方法与 hook 普通方法类似,只是不用写方法名而已,因为构造方法的方法名就是类名。

这里 hook Dog 类的构造函数

1public Dog(String name, int age) {
2    this.name = name;
3    this.age = age;
4}

编写代码:

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XC_MethodHook;
5import de.robv.android.xposed.XposedBridge;
6import de.robv.android.xposed.XposedHelpers;
7import de.robv.android.xposed.callbacks.XC_LoadPackage;
8
9public class HookTest implements IXposedHookLoadPackage {
10    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
11        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
12            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
13
14            XposedHelpers.findAndHookConstructor(clazz, String.class, int.class, new XC_MethodHook() {
15                @Override
16                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17                    param.args[0] = "逆刃";
18                    param.args[1] = 8;
19                }
20            });
21        }
22    }
23}

正常情况下,点击 Hook 构造方法按钮,会走下面代码的分支

1case R.id.bt_hookConstructMethod:
2    dog = new Dog("靓仔"15);
3    String dogString = dog.toString();
4    Toast.makeText(this, dogString, Toast.LENGTH_LONG).show();
5    break;

然后由于 hook 住了构造方法,并修改了里面的参数,所以输出结果就不是 靓仔,15 ,而是 逆刃,8。

Xposed Api详解到RPC的使用


获取和修改静态字段

获取静态字段,方法如下图

Xposed Api详解到RPC的使用

这里以获取 Dog 类的静态属性 type 为例

1static String type = "狗类";

编写代码:

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XC_MethodHook;
5import de.robv.android.xposed.XposedBridge;
6import de.robv.android.xposed.XposedHelpers;
7import de.robv.android.xposed.callbacks.XC_LoadPackage;
8
9public class HookTest implements IXposedHookLoadPackage {
10    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
11        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
12            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
13
14            Object type = XposedHelpers.getStaticObjectField(clazz, "type");
15            XposedBridge.log("获取类属性 type => " + type + "");
16        }
17    }
18}

成功获取到类属性

Xposed Api详解到RPC的使用

修改静态字段,与获取静态字段相似,只不过方法由 get 变为 set

修改静态字段,方法如下图

Xposed Api详解到RPC的使用

这里以修改 Dog 类的静态属性 type 为例

编写代码:

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XposedHelpers;
5import de.robv.android.xposed.callbacks.XC_LoadPackage;
6
7public class HookTest implements IXposedHookLoadPackage {
8    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
9        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
10            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
11
12            XposedHelpers.setStaticObjectField(clazz, "type""猫类");
13        }
14    }
15}

此时点击 HOOK 构造方法,弹出的就不是狗类,而是猫类

Xposed Api详解到RPC的使用

调用静态方法

调用静态方法,使用的方法如下图

Xposed Api详解到RPC的使用

这里举的例子,都调用 work 方法

可以看到 work 方法有重载,有两个 work 方法

1public static String work(String w) {
2    return w;
3}
4
5public static String work(String w, int h) {
6    String str = "狗正在" + w + "已工作" + h + "小时";
7    return str;
8}

只需要在调用时,按参数的类型,严谨填入即可,无需考虑重载,想调用哪个就按哪个的参数类型进行填写。

分别调用上面两个 work 方法,代码如下:

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XposedBridge;
5import de.robv.android.xposed.XposedHelpers;
6import de.robv.android.xposed.callbacks.XC_LoadPackage;
7
8public class HookTest implements IXposedHookLoadPackage {
9    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
10        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
11            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
12
13            Object work = XposedHelpers.callStaticMethod(clazz, "work""狗正在看家");
14            XposedBridge.log("调用静态方法 work => " + work);
15
16            Object workOverload = XposedHelpers.callStaticMethod(clazz, "work""狗正在玩耍"50);
17            XposedBridge.log("调用静态方法 work => " + workOverload);
18        }
19    }
20}

可以看到 work 的两个重载方法都可成功调用

Xposed Api详解到RPC的使用


获取和修改动态字段

动态字段就是要有对象,而不能靠类直接进行调用。

所以首先要解决的第一个难点就是,获取对象!

获取对象的方式有两种

第一种方式就是使用 XposedHelpers.newInstance ,new 一个对象出来

第二种方式就是在 hook 方法时,使用 param.thisObject 获取对象

获取动态字段和获取静态字段方式的差异性只是少了 static

获取调用 eat 方法对象的 name,和 age 字段。编写代码:

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XC_MethodHook;
5import de.robv.android.xposed.XposedBridge;
6import de.robv.android.xposed.XposedHelpers;
7import de.robv.android.xposed.callbacks.XC_LoadPackage;
8
9public class HookTest implements IXposedHookLoadPackage {
10    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
11        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
12            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
13
14            XposedHelpers.findAndHookMethod(clazz, "eat", String.class, int.class, new XC_MethodHook() {
15                @Override
16                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17                    Object name = XposedHelpers.getObjectField(param.thisObject, "name");
18                    int age = XposedHelpers.getIntField(param.thisObject, "age");
19
20                    XposedBridge.log("获取实例属性 name => " + name);
21                    XposedBridge.log("获取实例属性 age => " + age);
22                }
23            });
24        }
25    }
26}

从上面的开发可知,调用的是下面的分支

1case R.id.bt_hookOverloadEat:
2    dog = new Dog("哈利"11);
3    String eatOverLoadResult = dog.eat("狗粮"50);
4    Toast.makeText(this, eatOverLoadResult, Toast.LENGTH_LONG).show();
5    break;

所以获取到的 name 是哈利,age 是 11

Xposed Api详解到RPC的使用

修改动态字段,就是将 set 变为 get

修改调用 eat 方法对象的 name,和 age 字段。

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XC_MethodHook;
5import de.robv.android.xposed.XposedBridge;
6import de.robv.android.xposed.XposedHelpers;
7import de.robv.android.xposed.callbacks.XC_LoadPackage;
8
9public class HookTest implements IXposedHookLoadPackage {
10    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
11        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
12            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
13
14            XposedHelpers.findAndHookMethod(clazz, "eat", String.class, int.class, new XC_MethodHook() {
15                @Override
16                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17                    XposedHelpers.setObjectField(param.thisObject, "name""多多");
18                    XposedHelpers.setIntField(param.thisObject, "age"6);
19                }
20            });
21        }
22    }
23}

通过 objection 搜索对象获取到 name 已经变成了多多,age 变成了 6。

Xposed Api详解到RPC的使用


调用普通方法

调用普通方法,使用的方法如下图

Xposed Api详解到RPC的使用

首选运用是通过 new 对象,来调用 count 方法

1Object dog = XposedHelpers.newInstance(clazz, "哈里"12);
2Object count = XposedHelpers.callMethod(dog, "count"2211);
3XposedBridge.log("调用普通方法 work => " + count);

调用普通方法成功,得到的结果是

Xposed Api详解到RPC的使用

运行第二种方法,通过 hook 然后使用 param.thisObject 获取对象,进行调用

 1package com.bmstd.xposed1;
2
3import de.robv.android.xposed.IXposedHookLoadPackage;
4import de.robv.android.xposed.XC_MethodHook;
5import de.robv.android.xposed.XposedBridge;
6import de.robv.android.xposed.XposedHelpers;
7import de.robv.android.xposed.callbacks.XC_LoadPackage;
8
9public class HookTest implements IXposedHookLoadPackage {
10    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
11        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
12            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
13
14            XposedHelpers.findAndHookMethod(clazz, "eat", String.class, int.class, new XC_MethodHook() {
15                @Override
16                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17                    Object count = XposedHelpers.callMethod(param.thisObject, "count"6655);
18                    XposedBridge.log("调用普通方法 work => " + count);
19                }
20            });
21        }
22    }
23}

同样调用普通方法也可成功,得到的结果是

Xposed Api详解到RPC的使用


Xposed结合NanoHttpd使用RPC

Xposed 本身是不支持 RPC 的,但 Xposed 的优点是,可以在代码中嵌入任意 Java 代码,与Android本身开发无差别。

所以可以在 Xposed 中结合 NanoHttpd 完成 RPC。

首先在 Android Studio 工程中的 build.grade 中引入依赖

1implementation 'org.nanohttpd:nanohttpd:2.3.1'

Xposed Api详解到RPC的使用

然后就可以使用 NanoHttpd 了。

Xposed 结合 NanoHttp 使用 RPC 原理如下图所示:

Xposed Api详解到RPC的使用

编写 RPC 代码,可以调用静态方法 work 和普通方法 eat

 1package com.bmstd.xposed1;
2
3import java.io.IOException;
4import java.util.Map;
5
6import de.robv.android.xposed.IXposedHookLoadPackage;
7import de.robv.android.xposed.XposedBridge;
8import de.robv.android.xposed.XposedHelpers;
9import de.robv.android.xposed.callbacks.XC_LoadPackage;
10import fi.iki.elonen.NanoHTTPD;
11
12public class HookTest implements IXposedHookLoadPackage {
13    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
14        // 判断包名
15        if (loadPackageParam.packageName.equals("com.bmstd.hookdog")) {
16            // 寻找类
17            Class clazz = loadPackageParam.classLoader.loadClass("com.bmstd.hookdog.Dog");
18            class App extends NanoHTTPD {
19                String msg = "";
20
21                // 这里的写法是不变的,变的只是端口
22                public App() throws IOException {
23                    super(8899); // 定义端口
24                    start(NanoHTTPD.SOCKET_READ_TIMEOUT, true); // 启动 NanoHTTPD
25                    XposedBridge.log("nRunning! Point your browsers to http://localhost:8899/ n");
26                }
27
28                // serve 就是处理请求和返回的地方,主要的逻辑就在 serve 里面写
29                @Override
30                public Response serve(IHTTPSession session) {
31                    // 获取参数,但是这里只能获取 get 参数
32                    Map<String, String> parameters = session.getParms();
33                    // 如果参数的键有 work,就调用静态方法 work
34                    if (parameters.containsKey("work")) {
35                        String work = parameters.get("work");
36                        msg = (String) XposedHelpers.callStaticMethod(clazz, "work", work);
37                    }
38
39                    // 如果参数的键有 foodName 和 amount,就调用普通方法 eat 
40                    if (parameters.containsKey("foodName") && parameters.containsKey("amount")) {
41                        Object dog = XposedHelpers.newInstance(clazz, "哈里"12);
42                        String foodName = parameters.get("foodName");
43                        String amount = parameters.get("amount");
44                        int i = Integer.parseInt(amount);
45
46                        msg = (String) XposedHelpers.callMethod(dog, "eat", foodName, i);
47                    }
48
49                    // 将结果返回页面
50                    return newFixedLengthResponse(Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, msg);
51                }
52            }
53
54            new App();
55        }
56    }
57}

由于 NanoHttpd 一定使用的是网络,所以被 hook 的 App 必须拥有网络权限,这点很重要,否则就会启动网络异常,报错误。

一定要在被 hook 的 App 中增加网络权限。这里就要在 com.bmstd.hookdog 设置网络权限。

Xposed Api详解到RPC的使用

此时通过网站就可以调用 Xposed 的主动调用了。

调用普通方法 eat 效果。

Xposed Api详解到RPC的使用

调用静态方法 work 效果。

Xposed Api详解到RPC的使用

这样就可以既隐藏代码细节,使用者又方便。

以上

That‘s all
更多系列文章
敬请期待

鸣谦安全,专注于安卓开发及逆向技术分享,每周准时更新原创技术文章,愿我们和意义中的自己相逢。喜欢记得关注我,及时获得最新推送,第三方转载请注明出处
想获取更多最新干货内容
每晚 19:00见
Xposed Api详解到RPC的使用
Xposed Api详解到RPC的使用

<<  向右滑动查看下一张图片  >>

 后台Xposed Api详解到RPC的使用 回复"资源"取干货
回复"
QQ群"一起闲聊

个人微信:bmstd6

添加请注明来意 Xposed Api详解到RPC的使用

真爱四连,BiuBiuBiu~
Xposed Api详解到RPC的使用

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月23日22:04:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Xposed Api详解到RPC的使用https://cn-sec.com/archives/1846848.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息