原文出处:http://d3adend.org/blog/?p=589
Stab 1: 检查是否安装hook框架,例如xposed或Substrate
PackageManager packageManager = context.getPackageManager();
List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for(ApplicationInfo applicationInfo : applicationInfoList) {
if(applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
Log.wtf("HookDetection", "Xposed found on the system.");
}
if(applicationInfo.packageName.equals("com.saurik.substrate")) {
Log.wtf("HookDetection", "Substrate found on the system.");
}
}
Stab 2: 检查调用栈
在方法中打印调用栈,看看有没插入可疑方法,
例如原来的调用栈为:
com.example.hookdetection.DoStuff->getSecret com.example.hookdetection.MainActivity->onCreate android.app.Activity->performCreate android.app.Instrumentation->callActivityOnCreate android.app.ActivityThread->performLaunchActivity android.app.ActivityThread->handleLaunchActivity android.app.ActivityThread->access$800 android.app.ActivityThread$H->handleMessage android.os.Handler->dispatchMessage android.os.Looper->loop android.app.ActivityThread->main java.lang.reflect.Method->invokeNative java.lang.reflect.Method->invoke com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run com.android.internal.os.ZygoteInit->main dalvik.system.NativeStart->mainxposed hook后:
com.example.hookdetection.DoStuff->getSecret de.robv.android.xposed.XposedBridge->invokeOriginalMethodNative de.robv.android.xposed.XposedBridge->handleHookedMethod com.example.hookdetection.DoStuff->getSecret com.example.hookdetection.MainActivity->onCreate android.app.Activity->performCreate android.app.Instrumentation->callActivityOnCreate android.app.ActivityThread->performLaunchActivity android.app.ActivityThread->handleLaunchActivity android.app.ActivityThread->access$800 android.app.ActivityThread$H->handleMessage android.os.Handler->dispatchMessage android.os.Looper->loop android.app.ActivityThread->main java.lang.reflect.Method->invokeNative java.lang.reflect.Method->invoke com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run com.android.internal.os.ZygoteInit->main de.robv.android.xposed.XposedBridge->main dalvik.system.NativeStart->mainSubstratehook后:
com.example.hookdetection.DoStuff->getSecret com.saurik.substrate._MS$MethodPointer->invoke com.saurik.substrate.MS$MethodPointer->invoke com.cigital.freak.Freak$1$1->invoked com.saurik.substrate.MS$2->invoked com.example.hookdetection.DoStuff->getSecret com.example.hookdetection.MainActivity->onCreate android.app.Activity->performCreate android.app.Instrumentation->callActivityOnCreate android.app.ActivityThread->performLaunchActivity android.app.ActivityThread->handleLaunchActivity android.app.ActivityThread->access$800 android.app.ActivityThread$H->handleMessage android.os.Handler->dispatchMessage android.os.Looper->loop android.app.ActivityThread->main java.lang.reflect.Method->invokeNative java.lang.reflect.Method->invoke com.android.internal.os.ZygoteInit$MethodAndArgsCaller->run com.android.internal.os.ZygoteInit->main com.android.internal.os.ZygoteInit->main dalvik.system.NativeStart->main检测代码:
try {
throw new Exception("blah");
}
catch(Exception e) {
int zygoteInitCallCount = 0;
for(StackTraceElement stackTraceElement : e.getStackTrace()) {
if(stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
zygoteInitCallCount++;
if(zygoteInitCallCount == 2) {
Log.wtf("HookDetection", "Substrate is active on the device.");
}
}
if(stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") &&
stackTraceElement.getMethodName().equals("invoked")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate.");
}
if(stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
stackTraceElement.getMethodName().equals("main")) {
Log.wtf("HookDetection", "Xposed is active on the device.");
}
if(stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
stackTraceElement.getMethodName().equals("handleHookedMethod")) {
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed.");
}
}
}
Stab 3:检查你的方法是否变成native
xposed会将要hook的方法本地化,再添加hook的方法,具体实现见 XposedBridge_hookMethodNative。Substrate的原理和它差不多。
for (ApplicationInfo applicationInfo : applicationInfoList) {
if (applicationInfo.processName.equals("com.example.hookdetection")) {
Set classes = new HashSet();
DexFile dex;
try {
dex = new DexFile(applicationInfo.sourceDir);
Enumeration entries = dex.entries();
while(entries.hasMoreElements()) {
String entry = entries.nextElement();
classes.add(entry);
}
dex.close();
}
catch (IOException e) {
Log.e("HookDetection", e.toString());
}
for(String className : classes) {
if(className.startsWith("com.example.hookdetection")) {
try {
Class clazz = HookDetection.class.forName(className);
for(Method method : clazz.getDeclaredMethods()) {
if(Modifier.isNative(method.getModifiers())){
Log.wtf("HookDetection", "Native function found (could be hooked by Substrate or Xposed): " + clazz.getCanonicalName() + "->" + method.getName());
}
}
}
catch(ClassNotFoundException e) {
Log.wtf("HookDetection", e.toString());
}
}
}
}
}
Stab 4:根据 /proc/[pid]/maps
来检查内存中已加载的共享库或jar包
try {
Set libraries = new HashSet();
String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
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());
}
以下是对应的anti-anti-hook:
1.hookPackageManager的检测方法,去掉可疑的包名
2.hook
getStackTrace
去掉调用栈里的可疑函数
3.hook
getModifiers 去掉native
4.hook jave.io相应的方法