文章目录
Xposed环境搭建Xposed简介Xposed原理Xposed的安装
Xposed插件开发Xposed插件编写流程Xposed开发之Hook构造函数相关API无参构造函数的hook有参构造函数的hook实际效果
Xposed开发之修改属性相关API修改静态字段和成员字段实际效果
Xposed开发之hook一般函数相关APIhook一般函数实际效果
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
//模块名称
//最低版本号
新建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 {
@Override
public 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);
}
@Override
protected 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"))
{
//获取当前的classloader
ClassLoader classLoader=loadPackageParam.classLoader;
//获取当前的Class
Class studentClass=classLoader.loadClass("com.example.hookdemo01.Student");
//hook无参构造函数 参数列表为空 第二个参数类型不需要传入
XposedHelpers.findAndHookConstructor(studentClass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("com.example.hookdemo01.Student() is called! beforeHookedMethod");
}
@Override
protected 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() {
@Override
protected 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);
}
@Override
protected 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() {
@Override
protected 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);
}
@Override
protected 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() {
@Override
protected 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);
}
@Override
protected 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 {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
//判断包名是否是要hook的包
if (loadPackageParam.packageName.equals("com.example.hookdemo01"))
{
//获取当前的classloader
ClassLoader classLoader=loadPackageParam.classLoader;
//获取当前的Class
Class studentClass=classLoader.loadClass("com.example.hookdemo01.Student");
//hook三个参数的构造函数 传入参数类型
XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,int.class,String.class,String.class,new XC_MethodHook() {
@Override
protected 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"))
{
//获取当前的classloader
ClassLoader classLoader=loadPackageParam.classLoader;
//获取当前的Class
Class studentClass=classLoader.loadClass("com.example.hookdemo01.Student");
//hook
XposedHelpers.findAndHookMethod(studentClass, "publicstaticfun", String.class, int.class, new XC_MethodHook() {
@Override
protected 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]);
}
@Override
protected 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()
调用静态函数
//获取当前的classloader
ClassLoader classLoader=loadPackageParam.classLoader;
//获取当前的Class
Class studentClass=classLoader.loadClass("com.example.hookdemo01.Student");
//调用静态函数
XposedHelpers.callStaticMethod(studentClass,"publicstaticfun","guishou",100);
调用成员函数
//获取当前的classloader
ClassLoader classLoader=loadPackageParam.classLoader;
//获取当前的Class
Class 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代码
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {
//对壳入口类的onCreate函数进行hook 拿到classloader
XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {
@Override
protected 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 传入拿到的classloader
XposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", finalClassLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {
@Override
protected 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);
}
@Override
protected 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() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);
}
@Override
protected 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插件的管理:XposedInstallerxposed对函数hook的根本原理:java函数变为native函数xposed框架拥有大量的apixposed框架特定的文件:运行库文件和链接库
github上检测Xposed的demo:XposedChecker