Xposed框架被很多人用來注入App做一些Hook操作,當然有相應的注入也必然存在對應的檢測(反調試)操作,之前在吾愛、看雪論壇上看到很多大佬花式突破Xposed檢測的手法,所以秉承“拿來主義”,匯總了一下各大App常見的Xposed的檢測手法和突破的方式(這里只講關于在Java層面檢測Xposed,深入到SO層作檢測之后再講)。
**Xposed檢測方案以及突破方式****
1. 遍歷App安裝列表檢測是否包含Xposed Installer
原理:當App獲取到系統權限的時候,可以獲取系統的所有運行中的App的列表,通過列表發現是否存在有Xposed相關的App(通常都是Xposed Installer相關的Apk,例如de.robv.android.xposed.installer)保持運行狀態,一旦存在,就表明用戶很有可能存在Hook行為。
解決方案:目前市面上的大多數手機廠都把應用權限暴露給用戶,所以用戶可以自定義App的權限,禁止相關的App獲取應用列表就可以防止App通過這個途徑檢測Xposed框架,當然,要過這個檢測可以修改Installer包名即可。
2. 通過自造異常檢測堆棧信息,讀取異常堆棧中是否包含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("");
} else if (result.contains("com.android.internal.os.ZygoteInit")) {
param.setResult("");
}
}
super.afterHookedMethod(param);
}
});
3. 通過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);
}
});
4. 由于Xposed的注入方式是通過底層SO文件修改被hook的方法為native來實現的,所以檢測方也可以通過檢測方法是否變成了native來達到檢測的目的。
原理:Xposed把Method的native func修改為它自己的處理函數,再這個函數中會回調Java層的handleHookMethod,處理函數鉤子,但是只有native函數,虛擬機才會走native func,所以Xposed會把java函數的修飾符修改為native,所以可以通過反射調用Modifier.isNative(method.getModifiers())方法可以校驗方法是不是native方法.
解決方案:因為檢測方必須要通過Modifier.isNative這個方式來做檢測,所以方法就是通過Hook isNative的方法,將檢測結果置為0就行
參考代碼:
modifyXposedHelpers.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);
}
});
5. 檢測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);
}
});
6. 通過反射XposedHelper類和XposedBridge類做信息檢測
原理:Xposed中有幾個比較常用的方法,findAndHookMethod等。通過反射找到要Hook的函數后會保存到XposedHelper類中的fieldCache、methodCache、constructorCache字段中。因此,可以通過反射遍歷XposedHelper類中的fieldCache、methodCache、constructorCache變量,讀取HashMap緩存字段是否有被Hook App的關鍵函數信息就行解決方案:檢測方通過反射調用XposedHelper的成員fieldCache中是否含有相關的關鍵字。
解決方案:修改類名,讓檢測方找不到相關類就行,可以參考第三種方案,修改類名參考代碼。
總結:各種手段的目的,最終都是解決如何定位Xposed檢測代碼的問題,最有效的方案就是搜索相關的關鍵詞,例如上述幾種檢測方案中說的某些關鍵詞,其他方法待后續總結。
對于對抗xposed檢測的問題,也可以使用FakeXposed等框架,對特定機器的framework層,進行修改,然后再安裝程序,進行調試。