Xposed
框架被很多人用来注入App
做一些Hook
操作,当然有相应的注入也必然存在对应的检测(反调试)操作,之前在吾爱、看雪论坛上看到很多大佬花式突破Xposed
检测的手法,所以秉承“拿来主义”,汇总了一下各大App
常见的Xposed
的检测手法和突破的方式(这里只讲关于在Java
层面检测Xposed
,深入到SO
层作检测之后再讲)。
App
安装列表检测原理:当App获取到系统权限的时候,可以获取系统的所有运行中的App的列表,通过列表发现是否存在有Xposed相关的App(通常都是Xposed Installer
相关的Apk,例如de.robv.android.xposed.installer
)保持运行状态,一旦存在,就表明用户很有可能存在Hook行为。
解决方案:目前市面上的大多数手机厂都把应用权限暴露给用户,所以用户可以自定义App的权限,禁止相关的App获取应用列表就可以防止App通过这个途径检测Xposed框架,当然,要过这个检测也可以修改Installer包名即可。
Xposed
字符串来识原理:在正常的Android系统启动过程中,init进程
会去解析init.rc
文件启动一系列的服务,其中就有app_process
进程,在app_process
执行过程中,会设置自身进程名为Zygote
,启动com.android.internal.os.ZygoteInit.Main
方法。而Xposed修改了app_process
进程,会先启动de.robv.android.xposed.XposedBridge.Main
方法,再由它去启动com.android.internal.os.ZygoteInit.Main
方法,因此堆栈信息中会多出一些内容。简单说就是Xposed
先于了Zygote
进程,因此在系统堆栈信息中会多出Xposed
相关的内容。
解决方案:通过Hook堆栈类StackTraceElement
,当发现Xposed
和Zygote
有错误输出时,修改输出信息,例如将输出置空来绕过错误信息检测。
参考代码:
XposedHelpers.findAndHookMethod(StackTraceElement.class, "getClassName", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String result = (String) param.getResult();
if (result != null){
if (result.contains("de.robv.android.xposed.")) {
param.setResult("");
// Log.i(tag, "替换了,字符串名称 " + result);
}else if(result.contains("com.android.internal.os.ZygoteInit")){
param.setResult("");
}
}
super.afterHookedMethod(param);
}
});
ClassLoader
的 loadClass
加载列表检测解决方案:通过Hook loadClass
加载类来修改加载的类名,例如修改de.robv.android.xposed
成另一个普通的包名
参考代码:
XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if(param.args != null && param.args[0] != null && param.args[0].toString().startsWith("de.robv.android.xposed.")){
// 改成一个不存在的类
param.args[0] = "de.robv.android.xposed.ThTest";
}
super.beforeHookedMethod(param);
}
});
hook
的方法为native
来实现的,所以检测方也可以通过检测方法是否变成了native
来达到检测的目的原理:Xposed
把Method
的nativefunc
修改为它自己的处理函数,再这个函数中会回调Java
层的handleHookMethod
,处理函数钩子,但是只有native
函数,虚拟机才会走nativefunc
,所以Xposed
会把java
函数的修饰符修改为native
,所以可以通过反射调用Modifier.isNative(method.getModifiers())
方法可以校验方法是不是native
方法
解决方案:因为检测方必须要通过Modifier.isNative
这个方式来做检测,所以方法就是通过Hook isNative的方法,将检测结果置为0就行了
参考代码:
// 定义全局变量 modify
XposedHelpers.findAndHookMethod(Method.class, "getModifiers", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Method method = (Method)param.thisObject;
String[] array = new String[] { "getDeviceId" };
String method_name = method.getName();
if(Arrays.asList(array).contains(method_name)){
modify = 0;
}else{
modify = (int)param.getResult();
}
super.afterHookedMethod(param);
}
});
XposedHelpers.findAndHookMethod(Modifier.class, "isNative", int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = modify;
super.beforeHookedMethod(param);
}
});
Xposed
相关文件原理:通过读取proc/self/maps
文件,在linux内核中,这个文件存储了进程映射了的内存区域和访问权限,因此遍历自身加载的库,就可以拿到当前上下文的so
和jar
列表,通过查找Xposed
相关文件来做检测
解决方案:因为读取的时候会调用BufferedReader
进行读取命令的内容,我们只需要Hook BufferedReader
过滤掉XposedBridge.jar
等相关内容就可以完成绕过。
参考代码:
XposedHelpers.findAndHookMethod(BufferedReader.class, "readLine", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String result = (String) param.getResult();
if(result != null) {
if (result.contains("/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar")) {
param.setResult("");new File("").lastModified();
}
}
super.afterHookedMethod(param);
}
});
原理:Xposed
中有几个比较常用的方法,findAndHookMethod
等。通过反射找到要Hook
的函数后会保存到XposedHelper
类中的fieldCache
、methodCache
、constructorCache
字段中。因此,可以通过反射遍历XposedHelper
类中的fieldCache
、methodCache
、constructorCache
变量,读取HashMap
缓存字段是否有被Hook App的关键函数信息就行
解决方案:检测方通过反射调用XposedHelper
的成员fieldCache
中是否含有相关的关键字,解决方案就是修改类名,让检测方找不到相关类就行,可以参考第三种方案,修改类名
参考代码:
无
关于如何定位,最有效的方案就是搜索相关的关键词,例如上述几种检测方案中说的某些关键词