Android反挂钩技术-Java层
原文: http://d3adend.org/blog/?p=589
翻译: Zhiwei(http://zhiwei.li/text/)
最近一个在native代码层(C/C++)检测 挂钩框架(hooking frameworks)的内部讨论,引发了我的思考:一个纯Java的Android应用,有什么方法来检测
Cydia Substrate或者Xposed framework的存在。
免责声明:
所有的这些反挂钩技术是 很容易被有经验的逆向工程师绕过的。我只是在探索人们可能会怎么去检测他们的Java应用程序已经被Substrate或者Xposed框架挂钩,因为在某些时候,我们需要能够绕过这些技术,来完成我们的工作,就像我们如何绕过root检测那样。我最后一次看着DexGuard和Arxan的Java保护产品(GuardIT),他们不支持任何挂钩框架的检测。我希望类似的反钩技术将来被添加到这些Java混淆/保护产品中。
1. 检查什么软件已经安装在设备上
浮现在脑海的第一个念头就是简单地检测Substrate或Xposed框架是否安装在设备上。我们可以向清楚包管理器查询安装的软件包,标记任何可疑的包,这是root检测中使用的一个常用技术。
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.");}}
2.检查可疑的方法调用栈跟踪(stack trace)。
接下来的技术是, 检查可疑的方法调用栈跟踪。下面的Java类,含有一个抛出异常的方法,捕获它,然后打印出栈跟踪。
public class DoStuff {public static String getSecret() {try {throw new Exception("blah");}catch(Exception e) {for(StackTraceElement stackTraceElement : e.getStackTrace()) {Log.wtf("HookDetection", stackTraceElement.getClassName() + "->" + stackTraceElement.getMethodName());}}return "ChangeMePls!!!";}}
进一步完善成
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.");}}}
3. 检查那些不应该是native的native方法 (在ART版本的Xposed中不适用)
Xposed框架,将要hooked的方法,改成native, 并用它自己的方法替代(调用hookedMethodCallback)
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());}}}}}
4. 用/proc/[pid]/maps 来检测 夹在到内存里的 恶意的共享对象 或者JARs
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());}
反-反挂钩
1)勾住 PackageManager的 getInstalledApplications 方法, 从列表里 删除恶意包 ,也就是说把它们隐藏起来
2)勾住 Exception的 getStackTrace 方法, 将恶意的 StackTraceElement 对象删除
3) 勾住 Method的 getModifiers, 调整标志,让方法看起来不是native的
4) 勾住 任何文件打开操作, 将 /proc/[PID]/maps 用 /dev/null 或者一个 bogus maps文件代替
http://zhiwei.li/text/2016/02/20/android%E5%8F%8D%E6%8C%82%E9%92%A9%E6%8A%80%E6%9C%AF-java%E5%B1%82/
更多相关文章
- Android(安卓)View框架的draw机制
- android 隔几秒再执行
- Handler部分原理
- EditText 的setKeyListener()方法的用法是输入某些特殊的字符
- [置顶] Canvas开篇之drawBitmap方法讲解
- android实战项目五做一个倒计时的button
- 据说程序员是最爱学习的群体,IT男都知道,这个行业日新月异,必须不断
- Android(安卓)9.0 的 recent 键/事件的拦截
- Android(安卓)WebView常见问题及解决方案汇总 .