Xposed检测与防范

分析抖音对xposed的检测技术

Posted by JovenHe on May 8, 2019

在Hook与反Hook的道路上参考大厂的技术是最为有效的,在我hook过的app也就微信和抖音居多了,正好昨天想分析一下抖音,所以Jadx打开抖音apk了,所以今天就来记录一下抖音的防Xposed实现。

严重声明 本文的意图只有一个就是通过分析app学习更多的逆向技术,如果有人利用本文知识和技术进行非法操作进行牟利,带来的任何法律责任都将由操作者本人承担,和本文作者无任何关系,最终还是希望大家能够秉着学习的心态阅读此文。

1.打开apk

电脑浏览器进入360应用市场,直接搜索抖音,下载apk文件,使用bin/jadx-gui打开apk,搜索文本一下,让子弹飞一会儿

2.搜索文本xposed

搜索xposed关键字.png

可以看到搜索中主要有两个文件中有所涉及

方法isInstallXposed

自造异常.png

可以看到这是很常见的自造异常检测方法。

通过自造异常,读取异常堆栈中是否包含xposed字符串来识别,但isInstallXposed方法并没有被用到。

类C29647a

/* renamed from: com.ss.sys.ces.c.a */
public class C29647a {
    /* renamed from: a */
    private static String f86072a = "a";
    /* renamed from: b */
    private static String f86073b = "XposedBridge.jar";
    /* renamed from: c */
    private static String f86074c = "de.robv.android.xposed.XposedBridge";
    /* renamed from: d */
    private static String f86075d = "com.saurik.substrate";
    /* renamed from: e */
    private static String f86076e = "com.saurik.substrate.MS$2";

    /* renamed from: a */
    public static boolean m56344a() {
        return C29647a.m56345a(f86073b) && C29647a.m56347c();
    }

    /* renamed from: a */
    private static boolean m56345a(String str) {
        try {
            String readLine;
            Set<String> hashSet = new HashSet();
            StringBuilder stringBuilder = new StringBuilder("/proc/");
            stringBuilder.append(Process.myPid());
            stringBuilder.append("/maps");
            BufferedReader bufferedReader = new BufferedReader(new FileReader(stringBuilder.toString()));
            while (true) {
                readLine = bufferedReader.readLine();
                if (readLine == null) {
                    break;
                } else if (readLine.endsWith(".so") || readLine.endsWith(".jar")) {
                    hashSet.add(readLine.substring(readLine.lastIndexOf(ZegoConstants.ZegoVideoDataAuxPublishingStream) + 1));
                }
            }
            bufferedReader.close();
            for (String readLine2 : hashSet) {
                if (readLine2.contains(str)) {
                    return true;
                }
            }
        } catch (Throwable unused) {
            return false;
        }
    }

    /* renamed from: b */
    public static boolean m56346b() {
        return C29647a.m56345a(f86075d) && C29647a.m56348d();
    }

    /* renamed from: c */
    private static boolean m56347c() {
        try {
            throw new Exception("");
        } catch (Exception e) {
            for (StackTraceElement className : e.getStackTrace()) {
                if (className.getClassName().equals(f86074c)) {
                    return true;
                }
            }
            return false;
        }
    }

    /* renamed from: d */
    private static boolean m56348d() {
        try {
            throw new Exception("");
        } catch (Exception e) {
            int i = 0;
            for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
                    i++;
                    if (i == 2) {
                        return true;
                    }
                }
                if (stackTraceElement.getClassName().equals(f86076e)) {
                    return true;
                }
            }
            return false;
        }
    }
}

可以看到 private static变量中两个是Xposed框架的,两个是Substrate框架的。

里面只有两个public static方法供外部调用,方法结果也都是boolean值。经分析,一个是检测Xposed,一个是检测Substrate,检测方法大致相同,现只说明Xposed的。

检测加载库

/* renamed from: a */
    private static boolean m56345a(String str) {
        try {
            String readLine;
            Set<String> hashSet = new HashSet();
            StringBuilder stringBuilder = new StringBuilder("/proc/");
            stringBuilder.append(Process.myPid());
            stringBuilder.append("/maps");
            BufferedReader bufferedReader = new BufferedReader(new FileReader(stringBuilder.toString()));
            while (true) {
                readLine = bufferedReader.readLine();
                if (readLine == null) {
                    break;
                } else if (readLine.endsWith(".so") || readLine.endsWith(".jar")) {
                    hashSet.add(readLine.substring(readLine.lastIndexOf(ZegoConstants.ZegoVideoDataAuxPublishingStream) + 1));
                }
            }
            bufferedReader.close();
            for (String readLine2 : hashSet) {
                if (readLine2.contains(str)) {
                    return true;
                }
            }
        } catch (Throwable unused) {
            return false;
        }
    }


m56345a方法是通过拼接”/proc/” Process.myPid() “/maps”,读取App自身加载的库中的jar和so文件查看是否含有XposedBridge.jar文件。

自造异常

    /* renamed from: c */
    private static boolean m56347c() {
        try {
            throw new Exception("");
        } catch (Exception e) {
            for (StackTraceElement className : e.getStackTrace()) {
                if (className.getClassName().equals(f86074c)) {
                    return true;
                }
            }
            return false;
        }
    }

m56347c方法是通过自造异常,读取异常堆栈中是否包含de.robv.android.xposed.XposedBridge字符串来识别是否被Xposed hook