參考文章:http://d3adend.org/blog/?p=589
Xposed和CydiaSubstrate是常用的兩款hook框架。其中Xposed能夠對java層進行hook,CydiaSubstrate能夠對java層和native層進行hook。我們如何檢測當前的手機已經安裝了相應的框架以及判斷當前進程是否被hook呢?方法只有一個,收集hook框架安裝及運行時在系統中留下的信息。
一、包名檢測
我們可以首先用PakageManager
類來檢測包名來判斷是否安裝了Xposed框架和CydiaSubstrate框架。
PackageManager packageManager=getApplicationContext().getPackageManager();
List<ApplicationInfo> appliacationInfoList=packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for(ApplicationInfo item:appliacationInfoList ){
if(item.packageName.equals("de.robv.android.xposed.installer")){
Log.wtf("HookDetection","Xposeded fonund on device");
}
if(item.packageName.equals("com.saurik.substrate")){
Log.wtf("HookDetection","Xposeded fonund on device");
}
}
繞過方法: hook packageManager.getInstalledApplications
二、檢查調用棧中的可疑方法
Xposed框架在hook應用的時候,會在stack trace中產生如下幾個可疑的方法:
1.當框架啓動的時候,de.robv.android.xposed.XposedBridge.main
方法會在dalvik.system.NativeStart.main
方法調用被調用
2.當Xposed框架hook一個特定的方法時,de.robv.android.xposed.XposedBridge.handleHookedMethod
和de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative
方法會被調用。
3. 被hook的方法會在調用棧中出現兩次。
Substrate框架在hook方法時會產生如下幾個可疑的方法:
1.當Substrate框架被激活時,com.android.internal.os.ZygoteInit.main
方法會被調用兩次。
2.當Substrate框架hook一個特定的方法時,com.saurik.substrate.MS$2->invoked
會被調用,com.saurik.substrate.MS$MethodPointer.invoke method
方法以及一個擴展的方法會被調用。
3. 被hook的方法會在調用棧中出現兩次。
知道正常調用棧和被hook調用棧的不同後,我們可以用java代碼來探測hook框架的存在。
try {
throw new Exception("Deteck hook");
} catch (Exception e) {
int zygoteInitCallCount = 0;
for (StackTraceElement item : e.getStackTrace()) {
// 檢測"com.android.internal.os.ZygoteInit"是否出現兩次,如果出現兩次,則表明Substrate框架已經安裝
if (item.getClassName().equals("com.android.internal.os.ZygoteInit")) {
zygoteInitCallCount++;
if (zygoteInitCallCount == 2) {
Log.wtf("HookDetection", "Substrate is active on the device.");
}
}
if (item.getClassName().equals("com.saurik.substrate.MS$2") && item.getMethodName().equals("invoke")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate.");
}
if (item.getClassName().equals("de.robv.android.xposed.XposedBridge")
&& item.getMethodName().equals("main")) {
Log.wtf("HookDetection", "Xposed is active on the device.");
}
if (item.getClassName().equals("de.robv.android.xposed.XposedBridge")
&& item.getMethodName().equals("handleHookedMethod")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed.");
}
}
}
繞過方法:hook getStackTrace()
三、檢測native方法中不應爲native的方法
Xposed框架通過將要hook的方法轉換成native方法,並替換成自己的代碼(調用hookedMethodCallback方法來代替) 。你可以查看app_process
文件中的 XposedBridge_hookMethodNative 方法來看看具體是如何工作的。
Substrate框架以相似的方式進行工作,它通過改變方法的屬性來進行hook。在ART模式下,Xposed框架不必要進行相關的轉換因爲方法已經是native方法。
假設我們聲明瞭一個公共方法
public class DoStuff {
public static String getSecret() {
return "ChangeMePls!!!";
}
}
當該方法被hook時,運行時中的類向下面的一樣
public class DoStuff {
public static String getSecret() {
return "ChangeMePls!!!";
}
}
這個異常的行爲在我們的包中能夠被探測到
1.找到應用DEXfile的位置
2.枚舉DEXfile文件中所有類
3.對於DEX文件,用反射來檢查不應該爲native 方法的natvie方法
四、用 /proc/[pid]/maps來探測內存中可疑的對象和JARs對象。
Set<String> libraries=new HashSet<String>();
String mapsFilename="/proc/"+android.os.Process.myPid()+"/maps";
try {
BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
String line;
while((line=reader.readLine())!=null){
if(line.endsWith(".so")||line.endsWith(".jar")){
int n = line.lastIndexOf(" ");
libraries.add(line.substring(n+1));
}
}
for(String library:libraries){
if(library.contains("com.saurik.substrate")) {
Log.wtf("HookDetection", "Substrate shared object found: " + library);
}
if(library.contains("XposedBridge.jar")) {
Log.wtf("HookDetection", "Xposed JAR found: " + library);
}
}
reader.close();
} catch (Exception e) {
Log.wtf("HookDetection", e.toString());
}
繞過方法:hook files open方法返回 /dev/null