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。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】