概述

Xposed是Android上的一个Hook框架,通过替换app_process来使得所有App进程在启动时就已经加载了Hook模块。从而达到在不侵入App程序的情况下,修改App的程序行为。这对App的完整性是有破坏的,如某些App需要统计其安装信息(如安装的设备数),通过Xposed就可以修改设备参数,进行设备伪装,从而干扰App的统计功能。

阅读Xposed的代码可以知道,Xposed框架hook程序的java函数的实现是将需要hook的java函数设置为函数,再申请一个内存空间用于保存该java函数的原来的信息,并把该空间的地址保存到该函数的java指令指针里。其核心代码如下:

c code
1
2
3
4
5
6
7
8
memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
// Replace method with our own code
SET_METHOD_FLAG(method, ACC_NATIVE);
method->nativeFunc = &hookedMethodCallback;
method->insns = (const u2*) hookInfo;
method->registersSize = method->insSize;
method->outsSize = 0;

从以上代码可以看出xposed的hook主要是把method的flag置为NATIVE标志,并且对nativeFunc指针进行了赋值(其用于实现函数劫持)。关键的地方还在与其把method的原有信息拷贝之后,将保存地址赋值给了insns。

检测方法

1. 简易的检测方法

了解xposed的实现过程之后,我们知道xposed框架要Hook一个函数,最先改变的就是这个函数的属性,由java函数变为了native函数。所以我们在判断一个函数是否被xposed框架hook的第一步就是判断其是否本应是一个java函数的函数被置为了native函数。
例如判断TelephonyManager.getDeviceId()是否被Hook,该函数常常被hook,来达到欺骗App的目的。

java code
1
2
3
4
5
6
7
8
9
10
11
12
TelephonyManager tm = getSystemService(”phone”);
Class clazz = tm.getClass();
Method m = clazz.getMethod(“getDeviceId”);
if (Modifier.isNative(m.getModifiers())) {
Log(“function is hooked”);
} else {
Log(“function is not hooked”);
}

上面的代码,通过判断java函数的属性是否被改为了native来判断函数被hook与否。


2. 更进一步的检测方式

由于客户机的环境各不相同,常常会出现各种各样的异常情况。所以仅仅判断java函数的属性是native属性就认为函数被hook也许过于草率。所以为了进一步验证函数是否被xposed所hook,我们可以在jni层做更多的判断。

从Xposed的实现过程看,其把原有的method结构体进行了拷贝保存,而把现有的结构体进行了一定的修改。而在jni层我们只要取得保存结构的内存空间,并把其与现有的结构进行对比,如果除去修改过的值之外,其余的值都一样,就可以判断其经过了xposed的处理。代码可以写为

c code
1
2
3
4
5
6
7
8
9
10
11
12
13
Method *current_method = MethodObject;
If (IsNative(current_method)) {
Method *original_method = &current_method->insns->method;
If (original_method->name == current_method->name) {
If (original_method->other_parameters == current_method\
->other_parameters) {
Log(“this method is hooked by xposed”);
}
}
}

3. 恢复被xposed修改的函数

由于已经确定函数被xposed修改,并且已经找到了保存原有method变量的内存空间,那么如果要恢复xposed的修改,只要把method变量进行还原就可以

c code
1
2
3
4
5
6
Method *current_method = MethodObject;
original_method = hookInfo->originalMethodStruct;
MemCopy(current_method, original_method, sizeof(method));

由于客户机器情况较为复杂,如果xposed的检测出错,只会发生误报的情况。而如果恢复出错,则会导致程序僵死。

其中恢复出错的情况,可能有xposed版本升级之后,代码实现发生改变,或者有开发者在xposed的基础上做了自己的修改,甚至还有手机厂商修改了底层变量的定义等情况。

所以在没有更加复杂细致的验证之前不能将xposed的恢复方法用于产品中。能够想到的方法是,启动一个专门的独立的App进程,进行恢复尝试。

考虑到开发的高效性以及系统效率和稳定,第一种简易的检测方式已经可以定位到大部分的异常情况,该方法可以在初版本中进行使用来作比较初级的检测。而更复杂的检测机制,则可以更加有针对性的识别主流的xposed框架的hook行为。