xposed 03 - hook字段与一般方法

本文主要讨论一下如何hook字段与方法

hook字段有两种方式:

  • 使用反射

  • 使用 xposed api

由于xposed 模块也运行在 app 进程中,所以我们可以将 app 的代码当作自己的,直接反射访问。

Hook静态字段与成员字段

测试代码:

复制代码
package com.example.hooktarge

class HookTarget2 {

    private var str: String = "hello"

    companion object {
        @JvmStatic
        private val id: Int = 10
    }

    override fun toString(): String {
        return "HookTarget2(str='$str')"
    }

}

使用 java 反射来更改静态字段:

复制代码
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
    if (loadPackageParam.packageName.equals("com.example.hooktarge")) {
      Class<?> aClass = loadPackageParam.classLoader.loadClass("com.example.hooktarge.HookTarget2");
            Field id = aClass.getDeclaredField("id");
            id.setAccessible(true);
            XposedBridge.log("HookTarget2 id = " + id.get(null));
            id.set(null, 42);
            XposedBridge.log("HookTarget2 id = " + id.get(null) + ", change by field set");
    }
}

输出结果:

复制代码
HookTarget2 id = 10
HookTarget2 id = 42, change by field set

使用java反射来更改成员字段:

复制代码
XposedHelpers.findAndHookConstructor("com.example.hooktarge.HookTarget2", loadPackageParam.classLoader, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
    }

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Object thisObject = param.thisObject;
        Field str = aClass.getDeclaredField("str");
        str.setAccessible(true);
        str.set(thisObject, "ass");
        XposedBridge.log(param.thisObject.toString());
    }
});

输出结果:

复制代码
HookTarget2(str='ass')

从上面的测试可以看到使用反射可以成功的更改字段。

但是反射使用起来比较麻烦,所以Xposed也提供了对应的api.

复制代码
int id1 = XposedHelpers.getStaticIntField(aClass, "id");
XposedBridge.log("HookTarget2 id = " + id1 + " get by api");
XposedHelpers.setStaticIntField(aClass, "id", 100);
XposedBridge.log("HookTarget2 id = " + XposedHelpers.getStaticIntField(aClass, "id") + " set by api");

Object str1 = XposedHelpers.getObjectField(thisObject, "str");
XposedBridge.log(str1 + " get by api");
XposedHelpers.setObjectField(thisObject, "str", "hhhhh");
XposedBridge.log(param.thisObject.toString() + "change by api");

使用内置的 api 就显得简洁多了。

Hook一般方法

java中有这样4种方法:

  • 普通类方法

  • 内部类方法

  • 匿名内部类方法

  • JNI方法

由于Android Art 虚拟机中,一个方法的表示都是 ArtMethod,只不过其执行的函数入口可以选择 jni 入口或者函数体入口,所以JNI方法与普通方法的 hook 是一样的。

内部类/匿名内部类这两个的不同之处在于类名要麻烦点,不过我们可以使用反编译工具拿到其类名。Java的中匿名内部类其实也是有名字的,在开发阶段确实看不到,但是在编译后会分配一个带数字的名字,所以其实内部类与匿名内部类的hook也是一样的。

看一个例子:

复制代码
package com.example.hooktarge;

import android.util.Log;

public class HookTarget3 {

    public void test() {
        String s = test1();
        Log.e("HookTarget3", s);
        String s1 = test2();
        Log.e("HookTarget3", s1);
        test3();
        test4();
    }

    class AbsClass {
        private String test1() {
            return "test1";
        }

        public int run() {
            return 1;
        }
    }

    private String test1() {
        return "test1";
    }

    private static String test2() {
        return "test2";
    }

    private void test3() {
        AbsClass absClass = new AbsClass();
        Log.e("HookTarget3", absClass.test1());
    }

    private void test4() {
        AbsClass absClass = new AbsClass() {
            @Override
            public int run() {
                return 2;
            }
        };

        int run = absClass.run();
        Log.e("HookTarget3", run + "");
    }

}

对普通方法与静态方法的 hook 是一样的:

复制代码
XposedHelpers.findAndHookMethod(
        "com.example.hooktarge.HookTarget3",
        loadPackageParam.classLoader,
        "test1",
        new XC_MethodHook() {

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        param.setResult("test11111111");
    }
});

XposedHelpers.findAndHookMethod(
        "com.example.hooktarge.HookTarget3",
        loadPackageParam.classLoader,
        "test2",
        new XC_MethodHook() {

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                param.setResult("test222222222");
            }
        });

但是对于内部类与匿名内部类方法,需要先确定其类名,我们使用 jadx 打开apk,发现它显示的很像源码,是看不出内部类的真正名字的:

复制代码
package com.example.hooktarge;

import android.util.Log;

/* loaded from: classes3.dex */
public class HookTarget3 {
    public void test() {
        String s = test1();
        Log.e("HookTarget3", s);
        String s1 = test2();
        Log.e("HookTarget3", s1);
        test3();
        test4();
    }

    /* JADX INFO: Access modifiers changed from: package-private */
    /* loaded from: classes3.dex */
    public class AbsClass {
        AbsClass() {
        }

        /* JADX INFO: Access modifiers changed from: private */
        public String test1() {
            return "test1";
        }

        public int run() {
            return 1;
        }
    }

    private String test1() {
        return "test1";
    }

    private static String test2() {
        return "test2";
    }

    private void test3() {
        AbsClass absClass = new AbsClass();
        Log.e("HookTarget3", absClass.test1());
    }

    private void test4() {
        AbsClass absClass = new AbsClass() { // from class: com.example.hooktarge.HookTarget3.1
            @Override // com.example.hooktarge.HookTarget3.AbsClass
            public int run() {
                return 2;
            }
        };
        int run = absClass.run();
        Log.e("HookTarget3", run + "");
    }
}

不过常做开发的也能自己拼出内部类的名字,就是使用 $ 连接符。我们切换到 smali 界面:

复制代码
new-instance v0, Lcom/example/hooktarge/HookTarget3$AbsClass;

new-instance v0, Lcom/example/hooktarge/HookTarget3$1;

这里就看到了,内部类的名字是 HookTarget3$AbsClass。

匿名内部类的名字是:HookTarget3$1,可以看到该匿名内部类分配的数字是 1,有兴趣的可以多写几个匿名内部类看看规律。

hook代码如下:

复制代码
XposedHelpers.findAndHookMethod(
        "com.example.hooktarge.HookTarget3$AbsClass",
        loadPackageParam.classLoader,
        "test1",
        new XC_MethodHook() {

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                param.setResult("test11111111");
            }
        });

XposedHelpers.findAndHookMethod(
        "com.example.hooktarge.HookTarget3$1",
        loadPackageParam.classLoader,
        "run",
        new XC_MethodHook() {

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                param.setResult(100);
            }
        });

输出log如下:

复制代码
test11111111
test222222222
test11111111
100

总结:

方法hook一律使用 XposedHelpers.findAndHookMethod(xxx) api,虽然文中并没有演示 jni 的hook(比较懒),但是实际上也是一样的。对于内部类与匿名内部类需要找准其类名后再 hook。

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
曹牧2 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法3 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7253 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎3 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄3 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea