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<String> 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<String> executeCommand(String[] shellCmd){     // 执行 linux 的 shell 命令
        String line = null;
        ArrayList<String> fullResponse = new ArrayList<String>();
        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<ApplicationInfo> 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
}


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容