修改属性

xiaoeryu Lv5

本章主要使用Xposed来修改类和类对象的属性

Xposed不止是可以实现对app自己实现的类构造函数进行hook,对于系统框架层的java函数也可以进行hook

加壳厂商一般使用的加载dex有,DexClassLoader、InMemoryDexClassLoader(8.0以后)、以及动态下发dex

我们先来看一下DexClassLoader

可以使用google提供的源码查看工具 查看源码

图一
  • 我们完全可以像hook其它app一样去hook源码中的函数
  • 从而可以监控到它都加载了哪些插件dex,或者通过它脱壳都是可以的
  • 之后也可以把FART和Xposed结合一下,通过Xposed去hook相关的构造函数,配合FART进行插件dex的脱壳和修复

Hook DexClassLoader

之前在JNI开发的时候写过一个测试demo使用DexClassLoader来动态加载dex这次我们来Hook它的DexClassLoader方法

图二

主要代码片段

// 在图一的源码里面可以看到DexClassLoader里面一共有四个参数(三个String + ClassLoader)
XposedHelpers.findAndHookConstructor(DexClassLoader.class, String.class, String.class, String.class, ClassLoader.class, new XC_MethodHook() {
                @Override
                // 		public Object thisObject;
                //		public Object[] args;
                //		private Object result = null;
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                    Object args[] = param.args;
                    String dexPath = (String) args[0];
                    String optimizedDirectory = (String) args[1];
                    String librarySearchPath = (String) args[2];
                    ClassLoader parent = (ClassLoader) args[3];
                    XposedBridge.log("HookDexClassLoader->beforeHookedMethod: " + dexPath + "---" + optimizedDirectory + "---" + librarySearchPath + "---" + parent);
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    DexClassLoader dexClassLoader = (DexClassLoader) param.thisObject;
                    XposedBridge.log("HookDexClassLoader->afterHookedMethod: " + dexClassLoader);
                }
            });

将这个插件编译后安装在设备上,在执行LoadDex的时候就会获取到通过DexClassLoader加载的dex文件信息

图三
  • 这样就可以获取到加载的dex信息

  • Xposed除了可以实现对app自己实现的类构造函数的hook,对于系统框架层的java函数也可以进行hook

Xposed插件修改类属性有两种办法
  1. 使用java反射修改属性:这种方式跟之前的章节中JNI开发中使用的api是一样的
  2. 使用Xposed的API修改属性:这种方式在内部其实也是调用的java反射的api来完成的,不过它里面已经封装过了我们调用起来比较简洁
            // 1. 使用java反射修改属性
            ClassLoader pathClassLoader = loadPackageParam.classLoader;
            Class stuClass = pathClassLoader.loadClass("com.xiaoeryu.xposedhook01.Student");
            XposedBridge.log("stuClass: " + stuClass.getName());

            Field teacherField = stuClass.getDeclaredField("teacher");
            teacherField.setAccessible(true);	// 这里如果是私有属性需要取消权限检查
            teacherField.set(null, "xiaoeryu666");

            String teacherName1 = (String) teacherField.get(null);
            XposedBridge.log("teacherField: " + teacherName1);

            // 2. 使用Xposed的API修改属性
            // setStaticObjectField(Class<?> clazz, String fieldName, Object value)
            XposedHelpers.setStaticObjectField(stuClass, "teacher", "xiaoeryu888");
            String  teacherName2 = (String) XposedHelpers.getStaticObjectField(stuClass, "teacher");
            XposedBridge.log("XposedHelpers.getStaticObjectField: " + teacherName2);
  • 使用Xposed的api来获取属性就不需要我们自己来处理权限检查的问题了,框架里面已经帮我们处理过了
图四
  • 这里可以看到我们分别使用两种都能获取和修改其属性

看一下LSPosed的源码,使用的setStaticObjectField方法,是怎么处理私有属性的权限检查的

在github上打开LSPposed的仓库直接搜我们调用的setStaticObjectField看它是怎么实现的

图五
  • 可以看到调用了findField跟进去看一下
图六
  • 这里也是调用了setAccessible来通过权限检查

修改对象属性

使用java反射的方式修改

跟之前使用反射修改属性的代码差不多,也是通过类找到对象修改它的值。这里修改的时候需要传入对象

Field nicknameField = stuClass.getDeclaredField("nickname");
nicknameField.setAccessible(true);
nicknameField.set(thisObj, "bear");

在apk这边可以打印出来查看

图七
使用Xposed的方式修改
XposedHelpers.setObjectField(thisObj,"nickname","duck");
  • 使用Xposed的api来修改非常的简介只需要一行代码就可以了,传入对象、要修改的Field、修改后的值

同样在apk那边可以看到打印的结果

图八

setObjectField的源码跟之前setStaticObjectField相比需要传入一个obj。都同样是调用了findField方法

图八

测试

接下来我们拿一个测试app来尝试通过Xposed的方式通过它的验证

用jadx打开查看一下

图九
  • 首先这里会拿到我们在编辑框输入的字符串进行check,如果通过就弹出Congratulations
  • 如果没有通过,就弹出Sorry,try again?

看一下check的验证过程

图十
  • 首先要我们输入的字符串长度等于16
  • 然后还要拿我们输入的content跟**Flag2()**进行字符串对比
  • Flag2()是一个无参的构造函数
图十一
  • 但是Flag2的长度不足16位

所以这里是不存在正确的Flag的,这里使用Xposed的方式hook这两处,使校验通过

public class HookFlag implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedBridge.log("HookFlag->app packagename" + loadPackageParam.packageName);
        if (loadPackageParam.packageName.equals("com.kanxue.xposedflag")) {
            XposedBridge.log("HookFlag->app packagename" + loadPackageParam.packageName);
            // 修改第一处校验的长度
            ClassLoader classLoader = loadPackageParam.classLoader;
            Class Flag1Class = classLoader.loadClass("com.kanxue.xposedflag.Flag1");
            XposedHelpers.setStaticIntField(Flag1Class, "length", 3);
            // 第二处是一个无参的构造函数,修改里面flag的值
            XposedHelpers.findAndHookConstructor("com.kanxue.xposedflag.Flag2", classLoader, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    super.beforeHookedMethod(param);
                    XposedBridge.log("xiaoeryu->beforeHookedMethod");
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    XposedBridge.log("xiaoeryu->afterHookedMethod");

                    Object Flag2obj = param.thisObject;
                    XposedHelpers.setObjectField(Flag2obj, "flag", "123");
                }
            });

        }
    }
}
  • 这里修改了两处校验的值,第一处是一个静态变量直接修改即可
  • 第二处是一个无参的构造函数用findAndHookConstructor方法在afterHookedMethod之后修改它的值

验证

在LSPosed中勾选上我们要测试的app,然后重启手机

重启后直接运行目标app,将刚刚修改后的值输入进去

图十二

验证通过

图十三
  • 标题: 修改属性
  • 作者: xiaoeryu
  • 创建于 : 2023-12-11 01:29:52
  • 更新于 : 2023-12-13 15:37:46
  • 链接: https://github.com/xiaoeryu/2023/12/11/修改属性/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论