Xposed开发技巧

Xposed,大名鼎鼎得Xposed,是Android平台上最负盛名的一个框架。在这个框架下,我们可以加载很多插件App,这些插件App可以直接或间接操纵系统层面的东西,比如操纵一些本来只对系统厂商才open的功能(实际上是因为Android系统很多API是不公开的,而第三方APP又没有权限)。有了Xposed后,理论上我们的插件APP可以hook到系统任意一个Java进程(zygote,systemserver,systemui好不啦!)。

lpparam.classLoader hook

  1. 静态field变量

静态field变量sMoney的值的修改和获取,可以直接使用xposed提供的XposedHelpers类相关功能函数。具体操作可以类比以下示例代码片段:

1
2
3
4
5
6
7
8
9
10
Class clazz =XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);
XposedHelpers.setStaticObjectField(clazz,"sMoney",110);
Field sMoney = clazz.getDeclaredField("sMoney");
sMoney.setAccessible(true);
System.out.println(sMoney.get(null));
  1. 隐藏函数hidden_fun

主动调用隐藏函数hidden_fun(这一类函数是指触发条件比较苛刻的函数,但是我们又需要了解它的输入、输出的大致关系),需要通过clazz来新建实例,最后将此实例与函数名组装成XposedHelpers.callMethod()的实参需求形式。具体操作可以类比以下示例代码片段:

1
2
3
Class clazz =XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);
XposedHelpers.callMethod(constructor.newInstance(),"hidden_fun");

以上代码仅适用于存在无参构造函数的类,如果目标类没有无参构造函数,那就麻烦一点了,需要根据构造函数参数类型,反射寻找构造函数,接着才能类似上述操作。具体操作可以类比以下示例代码片段:

假设此时的构造函数仅有以下函数,即public demo(){}不存在的情形:

1
2
3
4
5
6
7
8
public demo(String str){...}
Class clazz =XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);
Constructor constructor = clazz.getConstructor(String.class);
XposedHelpers.callMethod(constructor.newInstance("..."),"hidden_fun");
  1. 内部类inner_class
    内部类inner_class作为Android编程过程常见的一种编程方式,这里为了demo的全面,也将其列出。其实内部类整个处理过程与普通类极其相似,具体操作可以类比以下示例代码片段:
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
XposedHelpers.findAndHookMethod("com.example.inner_class_demo.demo$inner_class",lpparam.classLoader,
"secret", String.class, boolean.class, new XC_MethodHook() {
protectedvoid beforeHookedMethod(MethodHookParam param) throws Throwable {
for (int i = 0; i < param.args.length; i++) {
XposedBridge.log(" argument is:" + param.args[i]);
}
int field_result = (int)XposedHelpers.getObjectField(param.thisObject,"pMoney");
XposedBridge.log(String.valueOf(field_result));
}
});

需要注意的是,这里打印目标函数参数列表的时候,用了XposedBridge.log()。这样的输出方式对日志的长度有限制,即长度不超过1024。特殊场合(比如:文件读取时,需要查看文件的内容)需要注意处理下,不然会出现截断的现象。

  1. 匿名内部类$inner

同内部类,一般是class_name$1之类,具体可以反编译目标程序查看下。常见的反编译工具,比如:apktool、jeb、baksmali均可以方便达到目的。

  1. 构造函数

构造函数demo()的处理,可以使用xposed提供的XposedHelpers类,具体操作可以类比以下示例代码片段:

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
Class clazz =XposedHelpers.findClass("com.example.inner_class_demo.demo",lpparam.classLoader);
XposedHelpers.findAndHookConstructor(clazz, new XC_MethodHook() {
...
});
```
需要注意的是,由于构造函数不同于普通函数,函数名不需要提供(因为与类名相同,xposed框架处理函数名问题)。
6. 同时监控多函数
如果遇到某一类函数需要“批量”hook操作的时候,比如:需要同时监控多个构造函数、多个重载函数,我们此时不可能去挨个hook每个具体目标,那么应该怎么操作呢?我们可以这样来实现:
```java
hookAllConstructors(clazz, new XC_MethodHook() {
...
});
hookAllMethods(clazz, new XC_MethodHook() {
...
});

hook重载函数时候,只需要忽略参数的具体类型即可。这种方式其实可以达到两种效果:1. 高效的处理函数重载问题 2.目标函数参数类型太复杂,自定义的类型太多。忽略参数类型,可以简化我们的hook工作。

  1. 替换方法
    只要改变一下回调方式就行了,原来是用XC_MethodHook()回调函数,实现在调用方法前和方法后执行hook代码,而只要把这个回调函数变成XC_MethodReplacement()回调函数,就可以实现直接替换原方法的目的,那么只要在这个回调函数里不加任何代码,就可以实现调用原方法,而不执行其代码的目的。

attachBaseContext.classloader hook

  1. Multidex勾住技术
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
findAndHookMethod(Application.class, "attach", Context.class, newXC_MethodHook() {
@Override
protectedvoid afterHookedMethod(MethodHookParam param) throws Throwable {
// place your hooks here, it should work with lpparam.classLoader
findAndHookMethod(class, lpparam.classLoader, method, arg1, arg2, …new XC_MethodHook() {
@Override
protectedvoid beforeHookedMethod(MethodHookParam param)throws Throwable {
// this will be called before the clock was updated by
// the original method
Log.d("xposed", "劫持开始了~");
}
@Override
protectedvoid afterHookedMethod(MethodHookParam param)throws Throwable {
// this will be called after the clock was updated by
// the original method
}
});
}
});
  1. 加固APP的勾住
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
if (lpparam.packageName.equals("com.XX.XXbank")) {
XposedBridge.log("定位到hook程序");
XposedHelpers.findAndHookMethod("com.secneo.apkwrapper.ApplicationWrapper", lpparam.classLoader,
"attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protectedvoid afterHookedMethod(MethodHookParam param) throwsThrowable {
// TODO Auto-generated method stub
super.afterHookedMethod(param);
Context Ctx = (Context) param.args[0];
ClassLoader Clz = Ctx.getClassLoader();
XposedHelpers.findAndHookMethod("com.iss.sdpersonalbank.util.Log", Clz, "d", String.class,
String.class, new XC_MethodHook() {
@Override
protectedvoid beforeHookedMethod(MethodHookParam param) throws Throwable {
// TODO Auto-generated method stub
super.afterHookedMethod(param);
String Str1 = (String) param.args[0];
String Str2 = (String) param.args[1];
Log.e(TAG, Str1 + ":" + Str2);
}
});

网络资源学习。。。。