在 Android Studio 中新建项目,依次修改以下配置文件
插入以下内容,设置 xposed 模块的信息
1
2
3
|
<meta-data android:name="xposedmodule" android:value="true"/>
<meta-data android:name="xposeddescription" android:value="这是一个Xposed测试程序"/>
<meta-data android:name="xposedminversion" android:value="53"/>
|
插入依赖的仓库地址
1
|
maven { url "https://api.xposed.info/" }
|
插入依赖的包
Android Studio 老版本中使用的是 provided,但该关键字已被弃用,新版本使用 compileOnly 替代。
1
2
|
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
|
新建一个类,例如此处类名为 hook,功能为打印包名和进程名。此后我们的 hook 代码即在此编写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package dev.svip.xp;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class Hook implements IXposedHookLoadPackage {
@Override
public void-HandleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
Log.i("blog.svip.dev", lpparam.packageName);
Log.i("blog.svip.dev", lpparam.processName);
}
}
|
右键点击 “main” 文件夹 , 选择 new --> Folder -->Assets Folder,新建 assets 文件夹,新建文件 xposed_init,类型为 Text
添加我们编写的类名。如有多个,每行加入一个类名即可。
手机上安装后,在 Xposed 中激活模块并重启系统,即可生效。
手机重启后,我们可以看到 logcat 中有大量输出,将每个应用的包名和进程名都打印出来了。
首先,新建一个安卓项目,并创建 Teacher 类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package dev.svip.teacher;
public class Teacher {
int age = 0;
String name = "";
boolean hair = true;
public Teacher() {
}
public Teacher(int age) {
this.age = age;
}
public Teacher(int age, String name) {
this.age = age;
this.name = name;
}
public Teacher(int age, String name, boolean hair) {
this.age = age;
this.name = name;
this.hair = hair;
}
public String getString() {
return "\n " + "\nage:" + age + "\nname:" + name + "\nhair:" + hair;
}
}
|
在主函数中使用创建的类
1
2
3
4
5
6
7
8
9
|
Teacher a = new Teacher();
Teacher b = new Teacher(18);
Teacher c = new Teacher(20, "James");
Teacher d = new Teacher(22, "Tom", false);
Log.e("blog.svip.dev", a.getString());
Log.e("blog.svip.dev", b.getString());
Log.e("blog.svip.dev", c.getString());
Log.e("blog.svip.dev", d.getString());
|
当运行此 app 时,打印日志如下
我们在 xposed 模块中 hook Teacher 类的构造函数,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
public class hook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
//只hook目标应用
if (lpparam.packageName.equals("dev.svip.teacher")) {
//加载要hook的类
Class<?> myclass = lpparam.classLoader.loadClass("dev.svip.teacher.Teacher");
//hook构造函数,空参数
XposedHelpers.findAndHookConstructor(myclass, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.e("blog.svip.hook", "my teacher() is called");
//在xposed中打印日志
XposedBridge.log("my teacher() is called");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.e("blog.svip.hook", "my teacher() is over");
//在xposed中打印日志
XposedBridge.log("my teacher() is over");
}
});
//hook构造函数,参数为int
XposedHelpers.findAndHookConstructor(myclass, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//修改参数的值
Object[] args = param.args;
args[0] = 99;
Log.e("blog.svip.hook", "my teacher(99) is called");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.e("blog.svip.hook", "my teacher(99) is over");
}
});
//hook构造函数,参数为int,string
XposedHelpers.findAndHookConstructor(myclass, int.class, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//修改参数的值
Object[] args = param.args;
args[0] = 100;
args[1] = "xiaoming";
Log.e("blog.svip.hook", "my teacher(100, \"xiaoming\") is called");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.e("blog.svip.hook", "my teacher(100, \"xiaoming\") is over");
}
});
//hook构造函数,参数为int,string,boolen
XposedHelpers.findAndHookConstructor(myclass, int.class, String.class, boolean.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//修改参数的值
Object[] args = param.args;
args[0] = 101;
args[1] = "xiaoli";
args[2] = true;
Log.e("blog.svip.hook", "my teacher(100, \"xiaoli\", true) is called");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.e("blog.svip.hook", "my teacher(100, \"xiaoli\", false) is over");
}
});
}
}
}
|
加载模块,重启手机后,运行刚才的 app,打印日志如下,可以看出,构造函数已被成功 hook。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public class SetFieldValue implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("dev.svip.teacher")) {
//修改参数为int的构造函数中的属性
XposedHelpers.findAndHookConstructor("dev.svip.teacher.Teacher", lpparam.classLoader,
int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//需要在对象创建完成后进行修改,否则修改无效
super.afterHookedMethod(param);
Class<?> teachclas = lpparam.classLoader.loadClass("dev.svip.teacher.Teacher");
Field age = teachclas.getDeclaredField("age");
age.setAccessible(true);
age.set(param.thisObject, 999);
Field name = teachclas.getDeclaredField("name");
name.setAccessible(true);
name.set(param.thisObject, "zzzzzz");
Field hair = teachclas.getDeclaredField("hair");
hair.setAccessible(true);
hair.set(param.thisObject, true);
}
});
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class SetFieldValue implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("dev.svip.teacher")) {
XposedHelpers.findAndHookConstructor("dev.svip.teacher.Teacher", lpparam.classLoader,
int.class, String.class, boolean.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
XposedHelpers.setIntField(param.thisObject, "age", 188);
XposedHelpers.setObjectField(param.thisObject, "name", "xxxxxxxx");
XposedHelpers.setBooleanField(param.thisObject, "hair", true);
}
});
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class HookFunc implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("dev.svip.teacher")){
Class<?> teachcls = lpparam.classLoader.loadClass("dev.svip.teacher.Teacher");
XposedHelpers.findAndHookMethod(teachcls, "getString", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//获取原来的返回值并设置新的返回值
Object result = param.getResult();
param.setResult(result + "\nI am hooked!");
}
});
}
}
}
|
在 Teacher 类中添加一个内部类,并且修改 Teacher 类中的 getString 方法,使其调用 student 类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public String getString() {
student a = new student();
a.age = 10;
a.hair = true;
a.name = "mark";
return "\n " + "\nage:" + age + "\nname:" + name + "\nhair:" + hair + a.getString();
}
class student {
int age = 19;
String name = "mark";
String city = "beijing";
boolean hair = true;
public int getAge() {
return age;
}
public String getString() {
return "\ni am from student\n--age:" + age + "\n--name:" + name + "\n--hair:" + hair +
"\n--city:" + city;
}
}
|
运行效果如下
hook 内部类时,使用 $ 符号连接类名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class HookInnerClass implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("dev.svip.teacher")) {
XposedHelpers.findAndHookMethod("dev.svip.teacher.Teacher$student", lpparam.classLoader,
"getString", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
String str = "\nI am from hook student\nage:" + 999 + "\nname::" +
"blog.svip.dev" + "\nhair:" + "true" + "\ncity:" + "shanghai";
param.setResult(str);
}
});
}
}
}
|
hook 后的结果。
与 hook 内部类函数方式相同,函数名可以通过 GDA 逆向 apk 查看。
Java 层中的 JNI 函数与普通函数的 hook 方式相同。
在 Teacher 类中添加以下三个方法
1
2
3
4
5
6
7
8
9
|
public String publicmethod(){
return "I am from public method.";
}
private String privatemethod(){
return "I am from private method.";
}
public static String publicstatic(){
return "I am from public static method.";
}
|
在 Xposed 项目中,我们可以通过反射主动调用目标应用中的方法,也可以通过 Xposed 提供的 api 进行调用。
使用反射或者 Xposed api 直接调用即可
1
2
3
4
5
6
7
|
//反射
Method publicstatic = teacherclass.getDeclaredMethod("publicstatic");
String str = (String) publicstatic.invoke(null);
Log.e("blog.svip.dev", str);
//Xposed api
str = (String) XposedHelpers.callStaticMethod(teacherclass, "publicstatic");
Log.e("blog.svip.dev", str + " Called by xposed api.");
|
同样可以使用反射或者 Xposed api 调用,但是需要首先获得实例。以下提供四种获取实例的方式。
1
2
3
4
5
6
7
8
9
10
|
Object teacherobj = teacherclass.newInstance();
Method publicmethod = teacherclass.getDeclaredMethod("publicmethod");
str = (String) publicmethod.invoke(teacherobj);
Log.e("blog.svip.dev", str + " 默认无参构造方法");
Object teacherobj1 = teacherclass.getConstructor(int.class, String.class).newInstance(18, "小明");
Method privatemethod = teacherclass.getDeclaredMethod("privatemethod");
privatemethod.setAccessible(true);
str = (String) privatemethod.invoke(teacherobj1);
Log.e("blog.svip.dev", str + " 有参构造方法");
|
1
2
3
4
5
6
7
|
Object teacherobject = XposedHelpers.newInstance(teacherclass, 18);
str = (String) XposedHelpers.callMethod(teacherobject, "publicmethod");
Log.e("blog.svip.dev", str + " Called by xposed api. 有参构造方法");
Object teacherobject2 = XposedHelpers.newInstance(teacherclass);
str = (String) XposedHelpers.callMethod(teacherobject2, "privatemethod");
Log.e("blog.svip.dev", str + " Called by xposed api. 无参构造方法");
|
前面两种方法是创建一个新实例,而此种方法是 hook 已有的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Class<?> teacherclazz = lpparam.classLoader.loadClass("dev.svip.teacher.Teacher");
XposedHelpers.findAndHookConstructor(teacherclazz, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Method publicmethod = teacherclazz.getDeclaredMethod("publicmethod");
Method privatemethod = teacherclazz.getDeclaredMethod("privatemethod");
privatemethod.setAccessible(true);
String publicstr = (String) publicmethod.invoke(param.thisObject);
String privatestr = (String) privatemethod.invoke(param.thisObject);
Log.e("blog.svip.dev", publicstr + " Get instance by hook init.");
Log.e("blog.svip.dev", privatestr + " Get instance by hook init.");
}
});
|
当某些方法的参数是类的实例时,可以通过 hook 该方法,得到其参数,即可获得实例,从而主动调用类的方法。
我们在 app 的主函数中创建一个方法,并在主函数中调用它。
在 Xposed 中 hook getInfo 方法,其参数即为我们需要的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Class<?> mainActivity = lpparam.classLoader.loadClass("dev.svip.teacher.MainActivity");
Class<?> teacherClazz = lpparam.classLoader.loadClass("dev.svip.teacher.Teacher");
XposedHelpers.findAndHookMethod(mainActivity, "getInfo", teacherClazz, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Method publicmethod = teacherClazz.getDeclaredMethod("publicmethod");
Method privatemethod = teacherClazz.getDeclaredMethod("privatemethod");
privatemethod.setAccessible(true);
String publicstr = (String) publicmethod.invoke(param.args[0]);
String privatestr = (String) privatemethod.invoke(param.args[0]);
Log.e("blog.svip.dev", publicstr + " Get instance by hook method's param.");
Log.e("blog.svip.dev", privatestr + " Get instance by hook method's param.");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
|
1、https://www.freebuf.com/articles/terminal/189021.html