文章目录

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