原文: 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/

更多相关文章

  1. Android(安卓)View框架的draw机制
  2. android 隔几秒再执行
  3. Handler部分原理
  4. EditText 的setKeyListener()方法的用法是输入某些特殊的字符
  5. [置顶] Canvas开篇之drawBitmap方法讲解
  6. android实战项目五做一个倒计时的button
  7. 据说程序员是最爱学习的群体,IT男都知道,这个行业日新月异,必须不断
  8. Android(安卓)9.0 的 recent 键/事件的拦截
  9. Android(安卓)WebView常见问题及解决方案汇总 .

随机推荐

  1. Android使用Retrofit进行网络请求
  2. Android 给 app默认权限(不弹窗申请权限)
  3. Android(安卓)到底是个什么东西?
  4. Android 触摸提示音
  5. 细数Android Studio中使用junit4测试框架
  6. Android设置通知栏/状态栏透明改变通知栏
  7. Android 流式布局FlowLayout(搜索历史),
  8. Android 的 supportdesign
  9. android--多点触控的实现
  10. 【Android 开发教程】Toast通知