文章目录
-
- Xposed环境搭建
-
- Xposed简介
- Xposed原理
- Xposed的安装
- Xposed插件开发
-
- Xposed插件编写流程
- Xposed开发之Hook构造函数
-
- 相关API
- 无参构造函数的hook
- 有参构造函数的hook
- 实际效果
- Xposed开发之修改属性
-
- 相关API
- 修改静态字段和成员字段
- 实际效果
- Xposed开发之hook一般函数
-
- 相关API
- hook一般函数
- 实际效果
- Xposed开发之主动调用函数
-
- 相关API
- 调用静态函数
- 调用成员函数
- 实际效果
- Xposed开发之加壳APP处理
-
- 实际效果
- Xposed指纹检测
Xposed环境搭建
Xposed简介
Xposed是一款可以在不修改APK的情况下影响程序运行的框架,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。在这个框架下,我们可以编写并加载自己编写的插件APP,实现对目标apk的注入拦截等。
Xposed原理
控制zygote进程,通过替换/system/bin/app_precess程序控制zygote进程,使得它在系统启动的时候会加载Xposed framework的一个jar文件即XposedBridge.jar,从而完成对zygote进程及其创建的Dalvik/ART虚拟机的劫持,并且能够允许开发者独立的替代任何class。
Xposed的安装
4.4以下的Android版本安装较为简单,只需要两步:
- 对需要安装xposed的手机进行root
- 下载并安装xposedinstall,之后授予root权限,进入app点击安装即可
从Android 5.0开始,谷歌使用ART替换Dalvik,所以Xposed安装有点麻烦,分为两个部分:xposed*.zip和XposedInstaller.apk。zip文件是框架主体,需要进入Recovery后刷入,apk文件是xposed模块管理应用的,主要用于下载,激活,是否启用模块等功能管理。步骤如下:
- 完成对手机的root,并刷入recovery(比如twrp)
- 下载对应的zip补丁包,进入recovery刷入
- 重启手机,安装XposedInstaller并授予root权限即可
实际操作如下:
http://dl-xda.xposed.info/framework/
首先去官网下载对应的补丁包,sdk23对应Android 6.0版本,其他对应版本请自行查询,我这里用的是6.0的系统
然后选择对应的系统架构
版本选择最新版,接着按照上面的步骤安装xposed模块即可。
Xposed插件开发
Xposed插件编写流程
- 拷贝XposedBridgeApi.jar到新建工程的libs目录并导入模块
将jar包导入到模块,并设置Configuration为compileOnly
- 在AndroidMainfest.xml中增加Xposed相关内容
//是否配置为xposed插件 设置为true
<meta-data android:name="xposedmodule" android:value="true"/>
//模块名称
<meta-data android:name="xposeddescription" android:value="模块描述(例如:第一个xposed插件)"/>
//最低版本号
<meta-data android:name="xposedminversion"android:value="54"/>
- 新建hook类,编写hook代码
package com.example.xposeddemo01;import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;public class Xposed01 implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {}
}
实现IXposedHookLoadPackage接口,然后在handleLoadPackage函数内编写Hook代码
- 新建assets文件夹,然后在assets目录下新建xposed_init,在里面写上hook类的完整路径(包名+类名),可以有多个,每一个类写一行
Xposed开发之Hook构造函数
相关API
需要用到的API如下:
XposedHelpers.findAndHookConstructor
无参构造函数的hook
首先编写一个目标hook类,类代码包含多个构造函数
package com.example.hookdemo01;public class Student {String name;String id;int age;public Student(){name="default";id="default";}public Student(String name) {this.name = name;}public Student(String name, String id) {this.name = name;this.id = id;}public Student(String name, String id, int age) {this.name = name;this.id = id;this.age = age;}
}
然后打印出类的信息
package com.example.hookdemo01;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;public class MainActivity extends AppCompatActivity {public void PrintStudent(Student stu){Log.i("Xposed",stu.name+"--"+stu.id+"--"+stu.age);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Student studenta=new Student();Student studentb=new Student("GuiShou");Student studentc=new Student("GuiShou2","2021");Student studentd=new Student("GuiShou3","2021",20);PrintStudent(studenta);PrintStudent(studentb);PrintStudent(studentc);PrintStudent(studentd);}
}
接着编写hook代码,代码实现写在handleLoadPackage里
//判断包名是否是要hook的包if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//获取当前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//获取当前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hook无参构造函数 参数列表为空 第二个参数类型不需要传入XposedHelpers.findAndHookConstructor(studentClass, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student() is called! beforeHookedMethod");}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student() is called! afterHookedMethod");}});
有参构造函数的hook
然后编写有参函数的hook代码
//hook一个参数的构造函数 传入参数类型XposedHelpers.findAndHookConstructor(studentClass,String.class ,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//获取参数数组Object[] argsarray=param.args;String name=(String)argsarray[0];//打印参数名XposedBridge.log("com.example.hookdemo01.Student(String) is called! beforeHookedMethod--"+name);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String) is called! afterHookedMethod");}});//hook两个参数的构造函数 传入参数类型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//获取参数数组Object[] argsarray=param.args;String name=(String)argsarray[0];String id=(String)argsarray[1];//打印参数名XposedBridge.log("com.example.hookdemo01.Student(String,String) is called! beforeHookedMethod--"+name+"--"+id);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String) is called! afterHookedMethod");}});//hook三个参数的构造函数 传入参数类型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,int.class,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//获取参数数组Object[] argsarray=param.args;String name=(String)argsarray[0];String id=(String)argsarray[1];int age=(int)argsarray[2];//打印参数名XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! beforeHookedMethod--"+name+"--"+id+"--"+age);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! afterHookedMethod");}});
这里只需要调用findAndHookConstructor函数,完成回调函数编写即可
实际效果
启用xposed模块,重启目标app
可以看到这里打印出了所有相关的信息,如果需要还可以对参数进行修改
Xposed开发之修改属性
相关API
需要用到的API如下:
XposedHelpers.getStaticObjectField
XposedHelpers.setStaticObjectField
XposedHelpers.getObjectField
XposedHelpers.setObjectField
Xposed不只是可以实现对app自己实现的类构造函数的hook,对于类的属性字段也提供了一系列修改的API,首先修改目标类代码
package com.example.hookdemo01;
import android.util.Log;public class Student {String name;String id;int age;private String nickname;public static String teachername;public Student(String name, String id, int age, String teacher,String nickname) {this.name = name;this.id = id;this.age = age;this.nickname = nickname;teachername=teacher;Log.i("Xposed","构造函数--teachername:"+teachername);Log.i("Xposed","构造函数--nickname:"+nickname);}
}
接着编写hook代码
修改静态字段和成员字段
package com.example.xposeddemo01;
import java.lang.reflect.Field;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;public class Xposed01 implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {//判断包名是否是要hook的包if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//获取当前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//获取当前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hook三个参数的构造函数 传入参数类型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,int.class,String.class,String.class,new XC_MethodHook() {@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! afterHookedMethod");//------------------------修改static属性-------------------------------------//设置teacher字段XposedHelpers.setStaticObjectField(studentClass,"teachername","teacher888");//获取teacher字段 打印输出String teacher = (String)XposedHelpers.getStaticObjectField(studentClass,"teachername");XposedBridge.log("修改后的teachername字段:"+teacher);//-------------------修改对象属性-----------------------------------------XposedHelpers.setObjectField(param.thisObject,"nickname","pandan888");//获取nickname字段 打印输出String nickname=(String)XposedHelpers.getObjectField(param.thisObject,"nickname");XposedBridge.log("修改后的nickname字段:"+nickname);}});}}
}
对于修改静态字段和成员字段,需要使用不同的API,xposed类内部已经取消了字段的访问检查,所以不需要自己取消检查,比反射修改字段的方案更加简洁
实际效果
Xposed开发之hook一般函数
相关API
XposedHelpers.findAndHookMethod
hook一般函数
首先修改一下目标app的代码
public static String publicstaticfun(String arg1,int arg2){//String result= privatestaticfun("test2",200);return arg1+"---"+arg2;}
增加一个静态的成员函数,接着编写hook代码
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//获取当前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//获取当前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hookXposedHelpers.findAndHookMethod(studentClass, "publicstaticfun", String.class, int.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//打印参数Object[] objectarray=param.args;String arg0=(String)objectarray[0];int arg1=(int)objectarray[1];XposedBridge.log("beforeHookedMethod---arg0:"+objectarray[0]+"arg1:"+objectarray[1]);//修改参数objectarray[0]="GuiShou";objectarray[1]=888;XposedBridge.log("beforeHookedMethod---arg0:"+objectarray[0]+"arg1:"+objectarray[1]);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);//打印返回值String result=(String)param.getResult();XposedBridge.log("afterHookedMethod---result:"+result);//修改返回值param.setResult("this it result");String result2=(String)param.getResult();XposedBridge.log("afterHookedMethod---result:"+result2);}});}}
实际效果
可以看到,这里已经打印出了修改前后的参数和返回值。实际上,类中的其他函数,例如私有成员函数,匿名内部类函数等等,都是用同样的方法进行hook,只要填入jadx反编译的类名和函数名即可。
Xposed开发之主动调用函数
public static String publicstaticfun(String arg1,int arg2){Log.i("xposed","publicstaticfun is call");return arg1+"---"+arg2;}private String privatefun(String arg1,int arg2){Log.i("xposed","privatefun is call");return arg1+"---"+arg2;}
首先修改代码,增加一个静态函数和一个成员函数,调用时触发log信息
相关API
XposedHelpers.callStaticMethod()
XposedHelpers.callMethod()
调用静态函数
//获取当前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//获取当前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//调用静态函数XposedHelpers.callStaticMethod(studentClass,"publicstaticfun","guishou",100);
调用成员函数
//获取当前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//获取当前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//调用构造函数 获取对象Object obj = XposedHelpers.newInstance(studentClass,"GuiShou3","2021",20,"teacher666","pandan666");//调用成员函数XposedHelpers.callMethod(obj,"privatefun","guishou",100);
实际效果
可以看到,静态函数和成员函数均被调用了。对于类中的静态函数,直接调用即可;对于成员函数,需要先得到类的实例,然后才能完成调用。
Xposed开发之加壳APP处理
对于加壳app的hook处理,实际上就是解决ClassLoader的问题。
对于加壳的APP,如果我们直接去hook当前的apk的话,那么壳代码中的ClassLoder必定是没有我们所需要hook的目标类。
由于壳代码的ClassLoader中并没有我们需要的类,所以在编写hook代码之前还需要一个步骤,就是在壳代码的修正ClassLoader之后,利用反射的方式拿到修正后的ClassLoader,然后传入修正后的ClassLoader,再对目标类进行hook
实际代码如下:
public static Field getClassField(ClassLoader classloader, String class_name,String filedName) {try {Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);return field;} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj,String filedName) {try {Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);Object result = null;result = field.get(obj);return result;} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}public static Object invokeStaticMethod(String class_name,String method_name, Class[] pareTyple, Object[] pareVaules) {try {Class obj_class = Class.forName(class_name);Method method = obj_class.getMethod(method_name, pareTyple);return method.invoke(null, pareVaules);} catch (SecurityException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}public static Object getFieldOjbect(String class_name, Object obj,String filedName) {try {Class obj_class = Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);return field.get(obj);} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NullPointerException e) {e.printStackTrace();}return null;}public static ClassLoader getClassloader() {ClassLoader resultClassloader = null;Object currentActivityThread = invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[]{}, new Object[]{});Object mBoundApplication = getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mBoundApplication");Application mInitialApplication = (Application) getFieldOjbect("android.app.ActivityThread",currentActivityThread, "mInitialApplication");Object loadedApkInfo = getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "info");Application mApplication = (Application) getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplication");resultClassloader = mApplication.getClassLoader();return resultClassloader;}
调用代码中的getClassloader函数,即可获取到修正后的classloader,接着编写hook代码
@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {//对壳入口类的onCreate函数进行hook 拿到classloaderXposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.stub.StubApp->onCreate afterHookedMethod");ClassLoader finalClassLoader=getClassloader();XposedBridge.log("finalClassLoader->" + finalClassLoader);GetClassLoaderClasslist(finalClassLoader);//再对目标类进行hook 传入拿到的classloaderXposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", finalClassLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);Object[] objectarray = param.args;String arg0 = (String) objectarray[0];int arg1 = (int) objectarray[1];XposedBridge.log("beforeHookedMethod11 privatefunc->arg0:" + arg0 + "---arg1:" + arg1);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);String result = (String) param.getResult();XposedBridge.log("afterHookedMethod11 privatefunc->result:" + result);}});Class personClass = XposedHelpers.findClass("com.kanxue.xposedhook01.Student$person", finalClassLoader);XposedHelpers.findAndHookMethod(personClass, "getpersonname", String.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("afterHookedMethod getpersonname->" + param.getResult());}});}});}}
首先需要hook入口类中的onCreate函数,在onCreate函数执行完成之后,获取到修正后的ClassLoader,接着再对目标类进行hook,此时hook的代码和hook一般函数一样,区别在于需要传入修正后的ClassLoder
实际效果
实际效果如图:可以看到这里已经成功对目标函数进行hook,打印出了hook之前和之后的函数相关信息。
Xposed指纹检测
可以根据下面的特征对xposed框架进行检测
- xposed插件的管理:XposedInstaller
- xposed对函数hook的根本原理:java函数变为native函数
- xposed框架拥有大量的api
- xposed框架特定的文件:运行库文件和链接库
github上检测Xposed的demo:XposedChecker