原文: 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/
本文探讨了如何在纯Java的Android应用中检测CydiaSubstrate或Xposed框架的存在,包括检查已安装软件、方法调用栈跟踪、native方法及内存中的共享对象等方法。
2661

被折叠的 条评论
为什么被折叠?



