首页
论坛
专栏
课程

[原创]QQ 浏览器 中的Hook,Root,模拟器,Debug,DexFile检测技术

2019-4-19 13:13 4069

[原创]QQ 浏览器 中的Hook,Root,模拟器,Debug,DexFile检测技术

2019-4-19 13:13
4069

在QQ浏览器的崩溃报告中,会发送大量当前设备的状态,设备的信息.硬件的信息.等等.

 

其中就包含了Hook检测,Root检测,模拟器检测,DexFile检测,Debug检测.这些信息.

 

由于这部分代码使用了名称混淆.所以我自己手工把代码还原了一遍.替换为有意义的名称.

 

部分逻辑修改了一下下.下面我们就依次来看.

Hook 检测

说是Hook检测,不如说是对目前比较流行的框架xposed,substrate的检测.

检测Package

这部分代码比较简单,就是检测是否安装了xposed或者substrate.

    public static int checkPackage(Context context) {
        int i = 0;
        PackageManager packageManager = context.getPackageManager();
        try {
            packageManager.getInstallerPackageName("de.robv.android.xposed.installer");
            i = 1;
        } catch (Exception e) {
        }
        try {
            packageManager.getInstallerPackageName("com.saurik.substrate");
            return i | 2;
        } catch (Exception e2) {
            return i;
        }
    }

检测/proc/mypid/maps

这里我对Linux的proc文件系统没有研究过的童鞋非常简单的搜一下盲.

 

首先proc文件系统的设计目的之一就是允许更方便的对进程信息进行访问.

 

每当一个进程创建的时候,/proc目录下就会有和该进程id对应的目录产生.

 

目录名称就是进程id.里面记录该进程的各种信息.其中maps记录了进程的

 

内存信息,更具体的说内存分段信息.具体什么含义我就不说了,反正我们可以知道它可以记录加载了那些模块就OK了.如下图

 

 

对Xposed有研究过的人应该都知道,xposed会对进程注入这些模块:

XposedBridge.jar
libxposed_art.so
app_process32_xposed

所以我们检测这些模块是否存在也是可以检测出xposed和substrate的.

public static int checkMap() throws Throwable {
        UnsupportedEncodingException unsupportedEncodingException;
        BufferedReader bufferedReader;
        Throwable th;
        int i = 0;
        BufferedReader bufferedReader2;
        int i2;
        int result = 0;
        try {
            HashSet hashSet = new HashSet();
            bufferedReader2 = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/" + android.os.Process.myPid() + "/maps"), "utf-8"));
            while (true) {
                try {
                    String readLine = bufferedReader2.readLine();
                    if (readLine == null) {
                        break;
                    } else if (readLine.endsWith(".so") || readLine.endsWith(".jar")) {
                        hashSet.add(readLine.substring(readLine.lastIndexOf(" ") + 1));
                    }
                } catch (UnsupportedEncodingException e) {
                    unsupportedEncodingException = e;
                    i2 = 0;
                    bufferedReader = bufferedReader2;
                    try {
                        unsupportedEncodingException.printStackTrace();
                        if (bufferedReader != null) {
                        }
                    } catch (Throwable th2) {
                        th = th2;
                        bufferedReader2 = bufferedReader;
                        if (bufferedReader2 != null) {
                        }
                        throw th;
                    }
                }
            }

            Iterator it = hashSet.iterator();
            while (it.hasNext()) {
                int i3;
                Object next = it.next();
                if (((String) next).toLowerCase().contains("xposed")) {
                    result = result | 64;
                }

                if (((String) next).toLowerCase().contains("com.saurik.substrate")) {
                    result = result | 128;
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

代码逻辑就是一行一行的读/proc/mypid/mpas.看是否包含xposed或com.saurik.substrate.

检测堆栈信息

先看图.

 

 

啊啊,我们只要检测堆栈是否包含红色圈圈de.robv.android.xposed.XposedBridge就可以了.

 

看代码

    public static int checkStackTraceElement() {
        int i = 0;
        try {
            throw new Exception("detect hook");
        } catch (Exception e) {
            int i2 = 0;
            for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && stackTraceElement.getMethodName().equals("main")) {
                    i2 |= 4;
                }
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && stackTraceElement.getMethodName().equals("handleHookedMethod")) {
                    i2 |= 8;
                }
                if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") && stackTraceElement.getMethodName().equals("invoked")) {
                    i2 |= 16;
                }
                if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
                    i++;
                    if (i == 2) {
                        i2 |= 32;
                    }
                }
            }
            return i2;
        }
    }

其实我们可以依据该方法检测自己的方法是否被Hook了.道理是一样的.就是检测堆栈是否包含有afterHookedMethod,beforeHookedMethod.

检测ActivityManagerNative

话说这个我不清楚啥原理.有懂得留言告诉我一下呗.

    public static int checkActivityManagerNative() {
        try {
            Method method = Class.forName("android.app.ActivityManagerNative").getMethod("getDefault", new Class[0]);
            method.setAccessible(true);
            if (method.invoke(null, new Object[0]).getClass().getName().startsWith("$Proxy")) {
                return 256;
            }
            return 0;
        } catch (Exception e) {
            return 256;
        }
    }

模拟器检测

    public static String getVMDesc()
    {
        StringBuilder stringBuilder = new StringBuilder();
        String VM = AdbShell.getprop("ro.genymotion.version");//判断genymotion模拟器
        if (VM != null) {
            stringBuilder.append("ro.genymotion.version");
            stringBuilder.append("|");
            stringBuilder.append(VM);
            stringBuilder.append("\n");
        }
        VM = AdbShell.getprop("androVM.vbox_dpi");//判断使用了vbox的模拟器,目前很多市面上的安卓模拟器都是基于vbox的
        if (VM != null) {
            stringBuilder.append("androVM.vbox_dpi");
            stringBuilder.append("|");
            stringBuilder.append(VM);
            stringBuilder.append("\n");
        }
        VM = AdbShell.getprop("qemu.sf.fake_camera");//检测安卓自身的模拟器
        if (VM != null) {
            stringBuilder.append("qemu.sf.fake_camera");
            stringBuilder.append("|");
            stringBuilder.append(VM);
        }
        return stringBuilder.toString();
    }

模拟器都具有一些特殊的属性.查找这个特殊的属性就可以判断是否是模拟器.

 

其中AdbShell.getprop 等效于 Systemproperties.get(name).

检测Debug

一个是检测debuggable标志.一个是检测TracerPid.

 //检测是否拥有调试属性
    public static String HaveDebugProp()
    {
        //ro.debuggable表示调试权限,默认为0,1表示可以调试
        StringBuilder builder = new StringBuilder();
        builder.append("ro.debuggable");
        builder.append(AdbShell.getprop("ro.debuggable"));
        return builder.toString();

    }

    //如果进程被调试TracerPid不为0
    public static String getTracerPid()
    {
        BufferedReader bufferedReader;
        String readLine = "";
        try {
            bufferedReader = new BufferedReader(new FileReader("/proc/self/status"));
            do{
                readLine = bufferedReader.readLine();
                if (readLine == null) {
                    break;
                }

            }while (!readLine.startsWith("TracerPid:"));
            readLine = readLine.substring(10).trim();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return readLine;
    }

Root检测

一个是检测是否有su文件.一个是检测属性.

private static final String[] suFiles = new String[]{"/su", "/su/bin/su", "/sbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/data/local/su", "/system/xbin/su", "/system/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/system/bin/cufsdosck", "/system/xbin/cufsdosck", "/system/bin/cufsmgr", "/system/xbin/cufsmgr", "/system/bin/cufaevdd", "/system/xbin/cufaevdd", "/system/bin/conbb", "/system/xbin/conbb"};

    public static boolean haveSu()
    {
        boolean z = false;
        boolean z2 = false;
        for (String file : suFiles) {
            if (new File(file).exists()) {
                z = true;
                break;
            }
        }


        if (Build.TAGS == null || !Build.TAGS.contains("test-keys")) {
            z2 = false;
        } else {
            z2 = true;
        }
        return z2 || z;
    }

    public static String RootCheckProp()
    {
        //ro.secure表示root权限,如果为0则表示启用root权限,1则相反
        //这个只能检测ROM被刷入时的默认属性.
        StringBuilder builder = new StringBuilder();
        builder.append("ro.secure:");
        builder.append(AdbShell.getprop("ro.secure"));
        builder.append("\n");
        builder.append("ro.adb.secure:");
        builder.append(AdbShell.getprop("ro.adb.secure"));
        builder.append("\n");
        return builder.toString();
    }

据说对一些比较难Root的手机厂商,修改rom里面的default.prop文件里的ro.secure为0,然后重签名再刷进去可以获得永久root.不过我没试过...,比较懒.

DexFile文件检测

根据ClassLoad检测加载了那些Dex文件

    //查询所有加载的dex,jar,apk文件,看一下是否有其他异己的模块加载
    public static void allDex()
    {
        Object pathList = getDeclaredFieldValue(DexFileCheck.class.getClassLoader(),"pathList");
        Object [] dexElements = (Object [])getDeclaredFieldValue(pathList,"dexElements");
        for(Object dex:dexElements)
        {
            DexFile dexFile = (DexFile)getDeclaredFieldValue(dex,"dexFile");
            if(dexFile == null)continue;
            Log.d(TAG, "allDex: found dexfile "+ dexFile.getName());
        }
    }

总结

其中这都是最简单最直接的检测方法,还有很多检测方法.但道理无非就一个,求同排异.

 

像不像人体的淋巴细胞.攻击者是病毒,而守护者就是淋巴系统.其实就是为了说明一个道理,

 

学会类比.



[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
打赏 + 4.00
打赏次数 2 金额 + 4.00
 
赞赏  雪衫   +2.00 2019/04/20 感谢分享~
赞赏  junkboy   +2.00 2019/04/19 感谢分享~
最新回复 (11)
子叶沙 2019-4-19 13:25
2
0
ActivityManagerNative是检测多开
junkboy 2019-4-19 13:28
3
0
感谢分享
你瞒我瞒 2019-4-19 14:29
4
0
感谢分享!!!!!!!!1
kxzpy 2019-4-19 14:44
5
0
QQ浏览器的崩溃报告 里面那里可以看到楼主的代码啊?小白求科普。。。。
芃杉 2019-4-19 14:51
6
0
mark
chpeagle 2019-4-19 17:31
7
0
kxzpy QQ浏览器的崩溃报告 里面那里可以看到楼主的代码啊?小白求科普。。。。
附件里面不就是代码了么。是一个Android Studio工程.还是说你想知道这部分smail代码在那里?
zlphaoren 2019-4-21 18:19
8
0
干货满满,感谢楼主分享。
最后于 2019-9-5 11:51 被zlphaoren编辑 ,原因:
tDasm 2019-4-22 09:01
9
0
学习
xxRea 2019-4-22 09:35
10
0
感谢分享
Jmdebugger 2019-4-22 20:31
11
0
感谢分享,不过这个检测有点弱
kxzpy 2019-5-3 20:02
12
0
chpeagle 附件里面不就是代码了么。是一个Android Studio工程.还是说你想知道这部分smail代码在那里?
这些 android studio的java代码是smail代码翻译过来的?
游客
登录 | 注册 方可回帖
返回