xposed安装和使用入门

因为xposed已经停止更新,高版本的android可以使用他的改良版lsposed,开发环境和xposed一致, 首先需要安装magisk + zygisk + lsposed.

xposed开发环境

导入xposed模块开发使用的jar包

  • 通过设置jcenter (未成功)
    设置app的build.gradle, 在dependencies里加上
dependencies {
    //低版本使用provided, 但是不能使用implementation
    compileOnly 'de.robv.android.xposed:api:82'
    compileOnly 'de.robv.android.xposed:api:82:sources'
}

在setting.gradle中添加(低版本的gradle是在build.gradle中添加)jcenter() 或者maven { url 'https://maven.aliyun.com/repository/public/' }

然后sync now后就会下载对应的xposed的jar包.

但是我在导入的时候遇见个问题, 就是无法导入和使用xpoesd jar包中的任何类. 具体原因还不清楚.

  • 直接导入XposedBridgeApi-82.jar

还有一种办法就是直接下载对应的jar包并导入使用, 下载的jar包放入lib目录中, 右键jar包选择Add As Library.

这样android studio会自动在build.gradle中添加implementation files('libs\\XposedBridgeApi-82.jar') , 这会使apk在编译时将jar包一起打包。因为xposed已经包含了对应的jar包, 所以在加载此xposed apk模块时会出现重复导入jar包并出错: Cannot load module。需要将implementation换成compileOnly,意思是仅在编译时有效不进行打包。

AndroidManifest.xml添加必要信息

//告诉xposed框架这是一个xposed模块
<meta-data
      android:name="xposedmodule"
      android:value="true" />
//给模块添加描述信息
<meta-data
      android:name="xposeddescription"
      android:value="这是一个xposed demo" />
//支持的最低的xposed版本
<meta-data
      android:name="xposedminversion"
      android:value="53" />

编写hook类

添加一个java类,此类实现IXposedHookLoadPackage接口并编写handleLoadPackage函数。handleLoadPackage回调函数会在apk加载时由xposed的在XposedInit函数调用,这里简单实现了打印当前加载apk的包名。

build后安装到手机上就可以在lsposed中看到这个xposed模块。

对指定apk启动此模块

重启apk后发现此xposed模块已经成功加载,日志也已经成功打印。

xposed插件开发

示例类为一个student类。

public class Student {
    String student_name;
    static int school_id = 0;
    public Student(){
        Log.i("reverccqin", "student_name :" + student_name + " ,school_id :" + school_id);
    }

    public Student(String name){
        student_name = name;
        Log.i("reverccqin", "student_name :" + student_name + " ,school_id :" + school_id);
    }

    public String SetSchoolIDAndGetName(int id){
        school_id = id;
        Log.i("revrccqin", "school_id :" + school_id);
        return student_name;
    }
}

hook构造函数

通过findAndHookConstructor函数hook无参和有参的构造函数。

public class StudentHook implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "无参构造hook success!");
                    }
                }
        );

        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "有参构造hook success!");
                    }
                }
        );
    }
}

运行apk并查看日志发现已经hook成功。

修改属性

通过hook对应类的构造函数来设置其静态属性和对象属性。当然对于静态属性而言甚至不需要通过任何类函数的hook就可以设置,对象属性也可以通过hook其他函数设置(并不一定非得是构造函数)。

public class Student {
    String student_name;
    static int school_id = 0;
    public Student(){
        Log.i("reverccqin", "student_name :" + student_name + " ,school_id :" + school_id);
    }

    public Student(String name){
        student_name = name;
        Log.i("reverccqin", "student_name :" + student_name + " ,school_id :" + school_id);
    }

    public String SetSchoolIDAndGetName(int id){
        school_id = id;
        Log.i("reverccqin", "school_id :" + school_id);
        return student_name;
    }
}

分别通过反射和xposed的接口设置静态属性和对象属性。

public class StudentHook implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "无参构造hook success!");

                        //通过反射设置static属性
                        Field school_id_Field = param.thisObject.getClass().getDeclaredField("school_id");
                        school_id_Field.setAccessible(true);
                        school_id_Field.setInt(null, 1);
                        //通过反射设置对象属性
                        Field student_name_Field = param.thisObject.getClass().getDeclaredField("student_name");
                        student_name_Field.setAccessible(true);
                        student_name_Field.set(param.thisObject, "li hua");
                    }
                }
        );

        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "有参构造hook success!");

                        //通过xposed接口设置static属性
                        XposedHelpers.setStaticObjectField(param.thisObject.getClass(), "school_id", 1);
                        //通过xposed接口设置对象属性
                        XposedHelpers.setObjectField(param.thisObject, "student_name", new String("xiao hong"));
                    }
                }
        );
    }
}

查看hook的日志发现反射和xposed接口调用都成功设置了属性,其实xposed接口本质也是通过反射设置的属性。

hook一般函数

hook一般函数和hook构造函数类似只不过调用的是函数findAndHookMethod函数。前面都是通过类名进行hook,这里通过另一种方式使用类类型来hook,hook函数并修改其参数和返回值。

public class StudentHook implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

        Class StudentClass = loadPackageParam.classLoader.loadClass("com.reverccqin.demo.Student");
        XposedHelpers.findAndHookMethod(
                StudentClass,
                "SetSchoolIDAndGetName",
                int.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        //修改参数
                        Object[] objectArray = param.args;
                        objectArray[0] = 8888;
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        super.afterHookedMethod(param);
                        //设置返回值
                        String student_name = "xxxx";
                        param.setResult(student_name);
                    }
                }
        );
    }
}

查看hook后的结果,函数的参数与返回值都已经被修改。

主动调用

和通过反射和xposed的接口设置静态属性和对象属性一样,主动调用也可以通过反射或者xposed接口(xposed接口也是通过反射调用的)。这里分别hook构造函数并在构造函数中主动调用SetSchoolIDAndGetName设置school_id。

public class StudentHook implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "无参构造hook success!");

                         */
                        //反射调用函数
                        Method SetSchoolIDAndGetNameMethod = param.thisObject.getClass().getDeclaredMethod("SetSchoolIDAndGetName", int.class);
                        SetSchoolIDAndGetNameMethod.setAccessible(true);
                        SetSchoolIDAndGetNameMethod.invoke(param.thisObject, 1111);
                    }
                }
        );

        XposedHelpers.findAndHookConstructor(
                "com.reverccqin.demo.Student",
                loadPackageParam.classLoader,
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        Log.i("reverccqin", "有参构造hook success!");

                        //通过xposed接口调用函数
                        XposedHelpers.callMethod(param.thisObject, "SetSchoolIDAndGetName", 2222);
                    }
                }
        );
    }
}

查看主动调用后SetSchoolIDAndGetName并设置school_id后打印的日志,发现主动调用设置school_id成功。】

加壳apk的hook

因为xposed hook了handlebindapplication函数并在此函数调用之前去调用所有的hook类的handleLoadPackage函数,handlebindapplication函数会进行一些初始化并调用Application的attachBaseContext函数。通常情况下壳代码是在自己的Application.attachBaseContext函数中修正classloader为自定义的classloader并动态加载源程序的dex文件。所以xposed在handlebindapplication调用之前获取的classloader并作为参数传递给handleLoadPackage函数并不是有效的classloader,所以对于加壳的apk需要通过反射获取修正后的真正的classloader。hook操作时使用此ClassLoader即可,其他操作与为加壳apk一致。

//通过反射获取LoadedApk对象
Class ActivityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = ActivityThreadClass.getDeclaredMethod("currentActivityThread");
Object ActivityThreadObject = currentActivityThreadMethod.invoke(null);
Field mPackagesField = ActivityThreadClass.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
ArrayMap mPackages = (ArrayMap) mPackagesField.get(ActivityThreadObject);
WeakReference wr = (WeakReference) mPackages.get(this.getPackageName());
//通过反射获取LoadedApk对象的mClassLoader字段
Class LoadedApkClass = Class.forName("android.app.LoadedApk");
Field mClassLoaderField = LoadedApkClass.getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
ClassLoader mClassLoader = mClassLoaderField.get(wr.get());

自定义classloader加载dex文件的hook

自定义classloader的hook和加壳apk的hook的情况很相似,重点就是找到正确的classloader。apk通过自定义classloader加载apk的dex文件,这个时候需要获取到apk自定义的classloader后才能对此自定义的classloader加载的dex文件中的java类进行hook。方法就是对DexClassLoader,PathClassLoade和InMemoryDexClassLoader的构造函数并获取到返回的classloader。

xposed对so函数的hook

xposed本身是一个java层的hook框架,因此使用xposed的接口只能实现对apk的java层代码的hook。要想进行native层(so文件)的hook需要结合其他hook 框架,例如android-inline-hook。apk一般通过System.LoadLibrary加载自己的so库,要想在so库中函数没有执行前就进行hook的话需要在利用xposed对System.LoadLibrary函数设置hook,并在此函数调用之后调用自己的回调函数并对native层的代码进行hook。

public class XposedHookTest implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedBridge.log("load apk: " + loadPackageParam.packageName);
        XposedHelpers.findAndHookMethod(
                "java.lang.System",
                loadPackageParam.classLoader,
                "loadLibrary",
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        //to native hook
                    }
                }
        );
    }
}

但是发现hook失败,显示被hook的apk调用System.Loadlibrary函数无法找到so文件。

java.lang.System.LoadLibrary的代码发现此函数会获取当前函数调用者的类然后作为参数传递给java.lang.Runtime.LoadLibrary0, java.lang.Runtime.LoadLibrary0会获取此调用者类的classloader并加载so文件。结合上一部的日志就发现一个问题,因为xposed hook了java.lang.System.LoadLibrary函数,所以在xposed调用了设置的before回调函数后会通过反射去调用被hook的java.lang.System.LoadLibrary,所以现在java.lang.Runtime.LoadLibrary0函数在获取调用者类的时候实际获取的是xposed的类,并近一步获取到xposed类的classloader去加载so文件。而xposed类是通过自定义的classloader加载的,所以此classloader的native路径并不包含目标apk的so文件路径,自然系统就无法找到需要加载的so文件。

所以不能对java.lang.System.LoadLibrary进行hook,应该对java.lang.Runtime.LoadLibrary0进行hook。这样系统在加载so文件时获取到的就是目标apk自己的classloader。

public class XposedHookTest implements IXposedHookLoadPackage {
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        XposedBridge.log("load apk: " + loadPackageParam.packageName);
        XposedHelpers.findAndHookMethod(
                "java.lang.Runtime",
                loadPackageParam.classLoader,
                "loadLibrary0",
                Class.class,
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        //to native hook
                    }
                }
        );
    }
}

xposed入门实例

一个测试apk有root检测和调试状态检测,需要输入一个正确的flag。拖入jadx中查看发现其调用sg.vantagepoint.a.c类的a,b,c函数进行root检测,调用sg.vantagepoint.a.b类的a函数进行调试状态检测。如果检测异常会调用sg.vantagepoint.uncrackable1.MainActivity类的a函数结束进程。

可以通过xposed hook类sg.vantagepoint.a.c的a,b,c函数和sg.vantagepoint.a.b类的a函数并返回FALSE来绕过检测。

//hook公共函数 sg.vantagepoint.a.c.a
XposedHelpers.findAndHookMethod(
        "sg.vantagepoint.a.c",
        loadPackageParam.classLoader,
        "a",
        new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("call method: sg.vantagepoint.a.c.a"); 
                super.afterHookedMethod(param);
                param.setResult(false);
            }
        }
);

也可以通过xposed hook类sg.vantagepoint.uncrackable1.MainActivity的a函数并进行替换,替换的函数什么也不错。

//hook私有函数 sg.vantagepoint.uncrackable1.MainActivity.a
XposedHelpers.findAndHookMethod(
        "sg.vantagepoint.uncrackable1.MainActivity",
        loadPackageParam.classLoader,
        "a",
        String.class,
        new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                return null;
            }
        }
);

apk调用verify函数,verify函数又会调用sg.vantagepoint.uncrackable1.a类的a函数验证flag是否正确。

sg.vantagepoint.uncrackable1.a.a调用sg.vantagepoint.a.a.a函数生成flag。

通过hook函数sg.vantagepoint.a.a.a并打印返回值即可得到flag。

//hook静态函数 sg.vantagepoint.a.a.a
XposedHelpers.findAndHookMethod(
        "sg.vantagepoint.a.a",
        loadPackageParam.classLoader,
        "a",
        byte[].class,
        byte[].class,
        new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.log("call method:sg.vantagepoint.a.a.a");
                super.afterHookedMethod(param);
                byte[] result = (byte[])param.getResult();
                String flag = new String(result);
                XposedBridge.log("flag : " + flag);
            }
        }
);

安装此xposed模块并对目标apk启动,通过绕过root检测并打印flag。

posted @ 2022-12-29 01:11  怎么可以吃突突  阅读(3010)  评论(1编辑  收藏  举报