Xposed 如何Hook静态变量or构造方法or复杂参数的方法or替换函数执行内容or内部类中的函数or匿名类

3,373 阅读5分钟

小知识,大挑战!本文正在参与“  程序员必备小知识  ”创作活动

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

Xposed框架Hook相关api的使用

首先创建测试类继承IXposedHookLoadPackage, 以下所有的hook方法都在handleLoadPackage方法内部进行调用

public class HookTest implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        XposedBridge.log("HOOK初体验:" + lpparam.processName + ":" + lpparam.packageName);
}

准备需要被hook的代码用于测试

public class HookDemo {
    private String Tag = "HookDemo";
    private static  int staticInt = 100;
    public  int publicInt = 200;
    private int privateInt = 300;

    public HookDemo(){
        this("NOHook");
        Log.d(Tag, "HookDemo() was called|||");
    }

    private HookDemo(String str){
        Log.d(Tag, "HookDemo(String str) was called|||" + str);
    }

    public void hookDemoTest(){
        Log.d(Tag, "staticInt = " + staticInt);
        Log.d(Tag, "PublicInt = " + publicInt);
        Log.d(Tag, "privateInt = " + privateInt);
        publicFunc("NOHook");
        Log.d(Tag, "PublicInt = " + publicInt);
        Log.d(Tag, "privateInt = " + privateInt);
        privateFunc("NOHook");
        staticPrivateFunc("NOHook");

        String[][] str = new String[1][2];
        Map map = new HashMap<String, String>();
        map.put("key", "value");
        ArrayList arrayList = new ArrayList();
        arrayList.add("listValue");
        complexParameterFunc("NOHook", str, map, arrayList);

        repleaceFunc();
        anonymousInner(new Animal() {
            @Override
            public void eatFunc(String value) {
                Log.d(Tag, "eatFunc(String value)  was called|||" + value);
                Log.d(Tag, "anonymoutInt =  " + anonymoutInt);
            }
        }, "NOHook");

        InnerClass innerClass = new InnerClass();
        innerClass.InnerFunc("NOHook");
    }

    public void publicFunc(String value){
        Log.d(Tag, "publicFunc(String value) was called|||" + value);
    }

    private void privateFunc(String value){
        Log.d(Tag, "privateFunc(String value) was called|||" + value);
    }

    static private void staticPrivateFunc(String value){
        Log.d("HookDemo", "staticPrivateFunc(Strin value) was called|||" + value);
    }

    private void complexParameterFunc(String value, String[][] str, Map<String,String> map, ArrayList arrayList)
    {
        Log.d("HookDemo", "complexParameter(Strin value) was called|||" + value);
    }

    private void repleaceFunc(){
        Log.d(Tag, "repleaceFunc will be replace|||");
    }

    public void anonymousInner(Animal dog, String value){
        Log.d(Tag, "anonymousInner was called|||" + value);
        dog.eatFunc("NOHook");
    }

    private void hideFunc(String value){
        Log.d(Tag, "hideFunc was called|||" + value);
    }
    private void argFunc(Objcet... value){
        Log.d(Tag, "argFunc was called|||" + value);
    }

    class InnerClass{
        public int innerPublicInt = 10;
        private int innerPrivateInt = 20;
        public InnerClass(){
            Log.d(Tag, "InnerClass constructed func was called");
        }
        public void InnerFunc(String value){
            Log.d(Tag, "InnerFunc(String value) was called|||" + value);
            Log.d(Tag, "innerPublicInt = " + innerPublicInt);
            Log.d(Tag, "innerPrivateInt = " + innerPrivateInt);
        }
    }
}
  1. 修改类中的私有静态变量
Class<?> clazz = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo", lpparam.classLoader);
XposedHelpers.setStaticIntField(clazz, "staticInt", 99);
  1. Hook无参构造函数
Class<?> clazz = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo", loadPackageParam.classLoader);
XposedHelpers.findAndHookConstructor(clazz, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("Haha, HookDemo constructed was hooked");
                //大坑,此时对象还没有建立,即不能获取对象,也不能修改非静态变量的值
                //XposedHelpers.setIntField(param.thisObject, "publicInt", 199);
                //XposedHelpers.setIntField(param.thisObject, "privateInt", 299);
            }
        });
  1. Hook有参构造函数,并修改参数
    XposedHelpers.findAndHookConstructor(clazz, String.class,  new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    param.args[0] = "Haha, HookDemo(str) are hooked";
                }
            });
  1. Hook公有方法publicFunc,并修改参数以及修改下publicInt和privateInt的值,再顺便调用一下隐藏函数hideFunc
XposedHelpers.findAndHookMethod(clazz, "publicFunc", String.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    param.args[0] = "Haha, publicFunc are hooked";
                    //修改成员变量的值
                    XposedHelpers.setIntField(param.thisObject, "publicInt", 199);
                    XposedHelpers.setIntField(param.thisObject, "privateInt", 299);
                  	//调用函数
                    XposedHelpers.callMethod(param.thisObject, "hideFunc", "Haha, hideFunc was hooked");

                   
                }
            });
  1. Hook私有方法privateFunc,并修改参数
 XposedHelpers.findAndHookMethod(clazz, "privateFunc", String.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    param.args[0] = "Haha, privateFunc are hooked";
                }
            });
  1. Hook私有静态方法staticPrivateFunc, 并修改参数
  XposedHelpers.findAndHookMethod(clazz, "staticPrivateFunc", String.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    param.args[0] = "Haha, staticPrivateFunc are hooked";
                }
            });
  1. Hook复杂参数函数complexParameterFunc
 Class fclass1 = XposedHelpers.findClass("java.util.Map", loadPackageParam.classLoader);
            Class fclass2 = XposedHelpers.findClass("java.util.ArrayList", loadPackageParam.classLoader);
            XposedHelpers.findAndHookMethod(clazz, "complexParameterFunc", String.class,
                    "[[Ljava.lang.String;", fclass1, fclass2, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            param.args[0] = "Haha, complexParameterFunc are hooked";
                        }
                    });
  1. Hook私有方法repleaceFunc, 并替换打印内容
   XposedHelpers.findAndHookMethod(clazz, "repleaceFunc", new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    Log.d("HookDemo", "Haha, repleaceFunc are replaced");
                    return null;
                }
            });
  1. Hook方法, anonymousInner, 参数是抽象类,先加载所需要的类即可
 Class animalClazz  = loadPackageParam.classLoader.loadClass("com.example.xposedhooktarget.Animal");
            XposedHelpers.findAndHookMethod(clazz, "anonymousInner", animalClazz, String.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log("HookDemo This is test");
                    param.args[1] = "Haha, anonymousInner are hooked";
                }
            });
  1. Hook匿名类的eatFunc方法,修改参数,顺便修改类中的anonymoutInt变量
XposedHelpers.findAndHookMethod("com.example.xposedhooktarget.HookDemo$1", clazz.getClassLoader(),
                    "eatFunc", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            param.args[0] = "Haha, eatFunc are hooked";
                            XposedHelpers.setIntField(param.thisObject, "anonymoutInt", 499);
                        }
                    });

  1. Hook内部类InnerClass的InnerFunc方法,修改参数,顺便修改类中的innerPublicInt和innerPrivateInt变量
final Class<?> clazz1 = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo$InnerClass", loadPackageParam.classLoader);
            XposedHelpers.findAndHookMethod(clazz1, "InnerFunc", String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            param.args[0] = "Haha, InnerFunc was hooked";
                            XposedHelpers.setIntField(param.thisObject, "innerPublicInt", 9);
                            XposedHelpers.setIntField(param.thisObject, "innerPrivateInt", 19);
                        }
                    });
  1. 对于一些延迟动态加载的代码,hook方式
XposedHelpers.findAndHookMethod("dalvik.system.DexFile", lpparam.classLoader, "loadClass",
                String.class, "java.lang.ClassLoader", new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        String className = (String) param.args[0];
                        Object result = param.getResult();
                        if (result != null && result instanceof Class) {
                            if ("com.alipay.mobile.nebulauc.impl.UCWebViewClient".equals(className)) {
                                // hookLoadWebViewClient(((Class) result).getClassLoader());
                            } 
                        }
                    }

                });
  1. Hook有可变参数的方法
   XposedHelpers.findAndHookMethod(clazz, "argFunc", Object[].class,new XC_MethodHook() {
                @Override
                protected Object afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                   
                }
            });

总结

  1. 无论hook公有私有方法还是静态方法 ,抑或可变参方法,甚至是带返回值的方法,统一使用findAndHookMethod这个Api

  2. xposed无法hook接口和抽象类方法

  3. Hook逻辑类可实现的接口有 :

    • 安卓系统启动的时候(使用 IXposedHookZygoteInit 接口)、

    • 一个新的app被加载的时候(使用 IXposedHookLoadPackage 接口)、

    • 一个资源被初始化的时候( 使用 IXposedHookInitPackageResources 接口 )

  4. 实现IXposedHookLoadPackage 并使用 XposedBridge.log 打印的日志,当启动第三方应用后,Android Studio控制台无法查看到日志信息,需要在xposed应用内进行查看

  5. 参数为基本数据类型时需要传对应包装类的type属性,比如int传Integer.TYPE, 否则提示找不到方法,如果参数为引用数据类型,那直接传xxx.class即可

    • beforeHookedMethod 会在调用原方法前执行,如果使用setResult则跳过原方法,并返回setResult参数中的值。

    • afterHookedMethod 会在调用原方法后执行,setResult可改变返回值

    • replaceHookedMethod 会完全替换原方法,即原方法不执行,且返回值可以直接return,setResult不生效。

注:每次修改完毕后需要重启才生效

Hook 修改方法返回值
 XposedHelpers.findAndHookMethod(clazz, method, new Object[]{new XC_MethodHook() {
      protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    param.setResult(result);  // 设置返回值
                }
            }});
Hook 获取方法返回值
XposedHelpers.findAndHookMethod(clazz, method, new Object[]{new XC_MethodHook() {
      protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    Object result = param.getResult(); //获取返回值
                }
            }});
Hook 获取方法传入的参数值
XposedHelpers.findAndHookMethod(claName, cl, "i", String.class, String.class, Object[].class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
         // 数组param.args存储的参数列表里的值
         XposedBridge.log(TAG + param.args[0]);
         XposedBridge.log(TAG + param.args[1]);
         XposedBridge.log(TAG + param.args[2]);
          }
  });
Hook 给方法传值
Class cla = XposedHelpers.findClass(className, loadPackageParam.classLoader);
Object com = XposedHelpers.callStaticMethod(cla, "getInstance");
XposedHelpers.callMethod(com, "setDebug", true); // 传入指定值
Hook 获取Intent的值
    private void hookGetIntent(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        try {
            XposedHelpers.findAndHookMethod("android.app.Activity", loadPackageParam.classLoader, "getIntent", new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    Intent sou = (Intent) param.getResult();
                    KLog.d("hookGetIntent:" + sou.toURI().toString());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
Hook 广播发送的Intent信息
    private void hookSendBroadcast(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        try {
            XposedHelpers.findAndHookMethod("android.content.ContextWrapper", loadPackageParam.classLoader, "sendBroadcast", Intent.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    Intent sou = (Intent) param.args[0];
                    KLog.d("sendBroadcast:" + sou.toURI().toString());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Xposed一般Hook的是默认的dex文件,但是现在很多的APP都有多个Dex文件,所以使用Xposed时,经常遇到类名路径正确却出现ClassNotFoundError找不到类的错误。要解决这个问题,需要获取对应Dex文件的上下文环境。

Android在加载dex文件后会创建一个Application类,然后会调用attach方法,attach方法的参数就是上下文context,而且attach方法是final方法,不会因为被覆盖而hook不到,拿到这个context就可以获取对应的classload,然后就可以顺利hook到multidex的类了。

XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        ClassLoader cl = ((Context) param.args[0]).getClassLoader(); // 获取ClassLoader
        Class<?> hookClass = null;
        try {
            hookClass = cl.loadClass(claName); // 获取Class
            // 使用cl 和 hookClass 完成hook
            XposedHelpers.setStaticIntField(hookClass, fieldName, val);
            XposedHelpers.findAndHookMethod(claName, cl, "i", String.class, String.class, Object[].class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log(TAG + param.args[0]);
                    XposedBridge.log(TAG + param.args[1]);
                    XposedBridge.log(TAG + param.args[2]);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」