参考文章: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 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()
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方法
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