root 检测、Xposed 检测、Cydia 检测

1. root 检测

public class Root_check {

    private static String LOG_TAG = "Wooo";

    public static void checkRoot() {
        try {
            /*  源码 adb.c 内
            int adb_main(int is_daemon)
    {
    ......
    property_get("ro.secure", value, "");
    if (strcmp(value, "1") == 0) {
        // don't run as root if ro.secure is set...
        secure = 1;
        ......
    }

    if (secure) {
        ......
             */
            Object obj = utils.invokeStaticMethod("android.os.SystemProperties", "get",  new Class[]{String.class}, new Object[]{"ro.secure"}); // ro.secure  service.adb.root
            Log.i("Wooo", "checkRoot -> " + obj);
            if (obj != null) {
                if (obj.equals("1")) {
                    Log.i("Wooo", "checkRoot may not root");
                }
                if (obj.equals("0")) {
                    Log.i("Wooo", "checkRoot mast rooted");
                }
            }

            checkRelease();
            checkSUfile();
//            checkRootWhichSU();   // 执行 which su
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void checkRelease() {
        String buildTags = Build.TAGS;
        Log.i("Wooo", "cheeckRelease tag is : " + buildTags);
        if (buildTags != null && buildTags.contains("test-keys")) {
            Log.i("Wooo", "cheeckRelease -> debug");
        }
        if (buildTags != null && buildTags.contains("release-keys")) {
            Log.i("Wooo", "cheeckRelease -> release");
        }
    }

    private static void checkSUfile() {
        // "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su"
        String file[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/", "/su/bin/"};
        for (int i = 0; i < file.length; i++) {
            String sNm = file[i] + "su";
            File f = new File(sNm);
            if (f.exists()) {
                Log.i("Wooo", "checkRoot " + sNm + " file exists");
            } else {
                Log.i("Wooo", "checkRoot " + sNm + " file no exists");
            }
        }
    }

    public static boolean checkRootWhichSU() {
        String[] strCmd = new String[] {"/system/xbin/which","su"};
        ArrayList execResult = executeCommand(strCmd);
        if (execResult != null){
            Log.i("Wooo","execResult="+execResult.toString());
            return true;
        }else{
            Log.i("Wooo","execResult=null");
            return false;
        }
    }

    private static ArrayList executeCommand(String[] shellCmd){     // 执行 linux 的 shell 命令
        String line = null;
        ArrayList fullResponse = new ArrayList();
        Process localProcess = null;
        try {
            Log.i(LOG_TAG,"to shell exec which for find su :");
            localProcess = Runtime.getRuntime().exec(shellCmd);
        } catch (Exception e) {
            return null;
        }
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
        BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
        try {
            while ((line = in.readLine()) != null) {
                Log.i(LOG_TAG,"–> Line received: " + line);
                fullResponse.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i(LOG_TAG,"–> Full response was: " + fullResponse);
        return fullResponse;
    }
}

2. Xposed 检测

public class Xposed_check {
    private static String TAG = "Wooo Xposed";
    private static StringBuffer sb = new StringBuffer();

    // https://blog.csdn.net/jiangwei0910410003/article/details/80037971
    // https://www.52pojie.cn/thread-691584-1-1.html
    // https://tech.meituan.com/android_anti_hooking.html
    // https://segmentfault.com/a/1190000009976827
    public static void checkXposed(Context ctx) {
        checkCache();
        checkJarClass();
        checkJarFile();
        disableHooks();
        checkMaps();
        checkPackage(ctx);
        checkException();
    }

    private static void checkPackage(Context ctx) {
        PackageManager packageManager = ctx.getPackageManager();
        List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
        for (ApplicationInfo applicationInfo : applicationInfoList) {
            if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
                Log.i(TAG, "found xposed package installed");
            }
        }
    }

    private static void checkException() {
        try {
            throw new Exception("xppp");
        } catch (Exception e) {
            for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge")) {       // stackTraceElement.getMethodName()
                    Log.i(TAG, "found exception of xposed");
                }
            }
        }
    }


/*
bool is_xposed()
{
   bool rel = false;
   FILE *fp = NULL;
   char* filepath = "/proc/self/maps";
   ...
   string xp_name = "XposedBridge.jar";
   fp = fopen(filepath,"r"))
   while (!feof(fp))
   {
       fgets(strLine,BUFFER_SIZE,fp);
       origin_str = strLine;
       str = trim(origin_str);
       if (contain(str,xp_name))
       {
           rel = true; //检测到Xposed模块
           break;
       }
   }
    ...
}
*/
    private static void checkMaps() {
        String jarName = "XposedBridge.jar";
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
            while (true) {
                String str = bufferedReader.readLine();
                if (str == null) {
                    break;
                }
                if (str.endsWith("jar")) {
                    if (str.contains(jarName)) {
                        Log.i(TAG, "proc/pid/maps find Xposed.jar -> " + str);
                    }
                }
//                if (str.contains("hack|inject|hook|call")) {      // 检测 maps 内的关键字
//
//                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void checkCache() {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        String xpHelper = "de.robv.android.xposed.XposedHelpers";

        Log.i(TAG, "checkCache IN");
        try {
            Object XPHelpers = cl.loadClass(xpHelper).newInstance();        // 在 nexus6 的 7.1 系统上获取失败,抛出异常
            if (XPHelpers != null) {
                filterField(XPHelpers, "fieldCache");
                filterField(XPHelpers, "methodCache");
                filterField(XPHelpers, "constructorCache");
            } else {
                Log.i(TAG, "cannot find Xposed framework");
            }
            Log.i(TAG, "cache content -> " + sb.length() + " -> " + sb);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void filterField(Object xpHelper, String cacheName) {
        try {
            Field f = xpHelper.getClass().getDeclaredField(cacheName);
            f.setAccessible(true);
            HashMap caMap = (HashMap)f.get(xpHelper);
            if (caMap == null) {
                return;
            }
            Set caSet = caMap.keySet();
            if (caSet.isEmpty()) {
                return;
            }
            Log.i(TAG, "filter -> " + cacheName + " , size -> " + caSet.size());
            Iterator iterator = caSet.iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                Log.i(TAG, "filter key -> " + key);
                if (key == null) {
                    continue;
                }
                key = key.toLowerCase();
                if (key.length() <= 0) {
                    continue;
                }
                if (key.startsWith("android.support")) {
                    continue;
                }
                if (key.startsWith("javax.")) {
                    continue;
                }
                if (key.startsWith("android.webkit")) {
                    continue;
                }
                if (key.startsWith("java.util")) {
                    continue;
                }
                if (key.startsWith("android.widget")) {
                    continue;
                }
                if (key.startsWith("sun.")) {
                    continue;
                }
                sb.append(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void checkJarFile() {
        File f = new File("/system/framework/XposedBridge.jar");
        if (f.exists()) {
            Log.i(TAG, "system may installed Xposed find jar file");
        } else {
            Log.i(TAG, "system not install Xposed cannot find jar file");
        }
    }

    private static void checkJarClass() {
        try {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Class clazz = cl.loadClass("de.robv.android.xposed.XposedBridge");

            if (clazz != null) {
                Log.i(TAG, "system installed Xposed Class");
            } else {
                Log.i(TAG, "system not install Xposed Class");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // disable
    private static void disableHooks() {
        Object obj2 = utils.getStaticFieldOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks");
        Log.i(TAG, "disableHooks seted  -> " + obj2);

        Log.i(TAG, "disableHooks seted ");
        utils.setStaticOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks", true);

        Object obj = utils.getStaticFieldOjbectCL("de.robv.android.xposed.XposedBridge", "disableHooks");
        Log.i(TAG, "disableHooks seted  -> " + obj);
    }
}

3. Cydia 检测

public class Cydia_check {
    private static String TAG = "Wooo Cydia";

    public static void checkCydia() {
        checkMaps();
    }

    /*  当检测到有对应的 so 文件后,然后根据路径去获取对应的函数地址,如果能获取,说明被加载。有 9 个导出函数。
    void* dlopen = lookup_symbol("/data/app-lib/com.saurik.substrate-2/libsubstrate-dvm.so", "MSJavaHookMethod");
void* lookup_symbol(char* libraryname,char* symbolname)
{
    void *imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);
    if (imagehandle != NULL){
        void * sym = dlsym(imagehandle, symbolname);
        if (sym != NULL){
            return sym; //发现Cydia Substrate相关模块
            }
      ...
}
    */
    private static void checkMaps() {
        String so1 = "libsubstrate.so";
        String so2 = "libsubstrate-dvm.so";
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/maps"));
            while (true) {
                String str = bufferedReader.readLine();
                if (str == null) {
                    break;
                }
                if (str.endsWith("so")) {
                    if (str.contains(so1)) {
                        Log.i(TAG, "proc/pid/maps find libsubstrate.so -> " + str);
                    }
                    if (str.contains(so2)) {
                        Log.i(TAG, "proc/pid/maps find libsubstrate_dvm.so -> " + str);
                    }
                }
//                if (str.contains("hack|inject|hook|call")) {      // 检测 maps 内的关键字
//
//                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 第二种方法:通过检测/proc/self/maps下的加载so库列表得到各个库文件绝度路径,通过fopen函数将so库的内容以16进制读进来放在内存里面进行规则比对,采用字符串模糊查找来检测是否命中黑名单中的方法特征码。
    // 参考美团:https://tech.meituan.com/android_anti_hooking.html
}


你可能感兴趣的:(root 检测、Xposed 检测、Cydia 检测)