安卓加固基础(三)

Xposed检测方法和绕过

1.检查安装目录的是否有xposedinstall

private static boolean findHookAppName(Context context) {
    
      
        PackageManager packageManager = context.getPackageManager();  
        List<ApplicationInfo> applicationInfoList = packageManager  
                .getInstalledApplications(PackageManager.GET_META_DATA);  
  //遍历应用列表,查看是否有xposed的包名
        for (ApplicationInfo applicationInfo : applicationInfoList) {
    
      
            if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
    
      
                Log.wtf("HookDetection", "Xposed found on the system.");  
                return true;  
            }  
        }  
        return false;  
    }  

反检测方法1
关闭应用权限使其无法获取安装目录包名
在这里插入图片描述

反检测方法2:修改xposed的包名
在这里插入图片描述

反检测方法3 直接用Androidkiller干掉判断(不是很适用,毕竟有很多应用都有壳)

2.检测xposed相关的jar和so

private static boolean findHookAppFile() {
    
      
        try {
    
      
            Set<String> libraries = new HashSet<String>();  
            String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";  
            BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));  
            String line;  
            while ((line = reader.readLine()) != null) {
    
      
            //循环遍历/proc/pid/maps,如果后缀为.so或者为.jar,就存入Map中
                if (line.endsWith(".so") || line.endsWith(".jar")) {
    
      
                    int n = line.lastIndexOf(" ");  
                    libraries.add(line.substring(n + 1));  
                }  
            }  
            reader.close(); 
            //遍历Map检测是否包含XposedBridge.jar
            for (String library : libraries) {
    
      
                if (library.contains("XposedBridge.jar")) {
    
      
                    Log.wtf("HookDetection", "Xposed JAR found: " + library);  
                    return true;  
                }  
            }  
        } catch (Exception e) {
    
      
            Log.wtf("HookDetection", e.toString());  
        }  
        return false;  
    }  

反检测方法1
通过hook BufferedReader.classreadline()方法,使其在读取/proc/pid/maps目录时不读取Xposed.jar
实现如下:

XposedHelpers.findAndHookMethod(BufferedReader.class, "readLine", new XC_MethodHook() {
    
    
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    
    
                String result = (String) param.getResult();
                if(result != null) {
    
    
                //如果ReadLine方法返回的结果为Xposed.jar,就设置其返回值为空
                    if (result.contains("/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar")) {
    
    
                        param.setResult("");
                        new File("").lastModified();
                    }
                }
 
                super.afterHookedMethod(param);
            }
        });

反检测方法2:使用virtualXposed框架

3.查看程序运行栈内容读取判断是否有Xposed等字符串

说明:由Xposed原理可知,Xposed执行会早于Zygote进程,因此查看系统堆栈时会有Xposed的栈信息

private static boolean findHookStack() {
    
      
        try {
    
      
            throw new Exception("findhook");  
        } 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.");  
                        return true;  
                    }  
                }  
                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.");  
                    return true;  
                }  
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge")  
                        && stackTraceElement.getMethodName().equals("main")) {
    
      
                    Log.wtf("HookDetection", "Xposed is active on the device.");  
                    return true;  
                }  
                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.");  
                    return true;  
                }  
            }  
        }  
        return false;  
    }  

反检测方法:原理和方法二类似通过hook StackTraceElement.classgetClassName()

XposedHelpers.findAndHookMethod(StackTraceElement.class, "getClassName", new XC_MethodHook() {
    
    
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    
    
                String result = (String) param.getResult();
                if (result != null){
    
    
                  // 替换字符串为空
                    if (result.contains("de.robv.android.xposed.")) {
    
    
                        param.setResult("");
                      
                    }else if(result.contains("com.android.internal.os.ZygoteInit")){
    
    
                        param.setResult("");
                    }
                }
 
                super.afterHookedMethod(param);
            }
        });

4.利用Xposed的注入方式

由于Xposed会将hook的方法修改为native方法,因此可以通过检测方法是否为native达到检测目的,该方案适用性不是很广,因为应用的方法只会在被hook时才会被转换为native方法

 private static boolean findhookso(){
    
    
        boolean isNative=false;
        try {
    
    
            Class<MainActivity> clz=MainActivity.class;
            Method method=clz.getMethod("toastMessage");
            isNative=Modifier.isNative(method.getModifiers());
        } catch (NoSuchMethodException e) {
    
    
            e.printStackTrace ();
        }
        return isNative;
    }

反检测方法:

final int modify=0;
//获得检测时的方法
XposedHelpers.findAndHookMethod(Method.class, "getModifiers", new XC_MethodHook() {
    
    
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    
    
                Method method = (Method)param.thisObject;
                String[] array = new String[] {
    
     "getDeviceId" };
                String method_name = method.getName();
                //asList(T... a)返回一个受指定数组支持的固定大小的列表。
                if(Arrays.asList(array).contains(method_name)){
    
    
                    modify = 0;
                }else{
    
    
                    modify = (int)param.getResult();
                }
 
                super.afterHookedMethod(param);
            }
        });
 //hook isNative方法,修改返回值
        XposedHelpers.findAndHookMethod(Modifier.class, "isNative", int.class, new XC_MethodHook() {
    
    
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
    
    
                param.args[0] = modify;
 
                super.beforeHookedMethod(param);
            }
        });

root检测

1.查看su是否存在

我们通常使用

adb shell 
su

实现获取Android系统的root权限,因此我们可以通过检测su是否存在,从而达到Android设备是否被root。

想要检测su是否存在可以通过以三个方法。

(1)检测关键的目录

public static boolean isDeviceRooted01() {
    
    
        File f = null;
        final String kSuSearchPaths[] = {
    
    "/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};
        try {
    
    
            for (int i = 0; i < kSuSearchPaths.length; i++) {
    
    
                f = new File (kSuSearchPaths[i] + "su");
                if (f != null && f.exists ()) {
    
    
                    Log.i (LOG_TAG, "find su in : " + kSuSearchPaths[i]);
                    return true;
                }
            }
        } catch (Exception e) {
    
    
            e.printStackTrace ();
        }
        return false;

(2)使用which命令检测

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

    }

public static ArrayList<String> executeCommand(String[] shellCmd) {
    
    
        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) {
    
    
            Log.d (LOG_TAG, e.toString ());
            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;
    }

(3)直接构造执行su命令

 public static boolean isDeviceRooted03() {
    
    
        Process process = null;
        DataOutputStream os = null;
        try {
    
    
            Log.i (LOG_TAG, "to exec su");
            process = Runtime.getRuntime ().exec ("su");
            os = new DataOutputStream (process.getOutputStream ());
            os.writeBytes ("exit\n");
            os.flush ();
            int exitValue = process.waitFor ();
            Log.i (LOG_TAG, "exitValue=" + exitValue);
            if (exitValue == 0) {
    
    
                return true;
            } else {
    
    
                return false;
            }
        } catch (Exception e) {
    
    
            Log.i (LOG_TAG, "Unexpected error - Here is what I know: "
                    + e.getMessage ());
            return false;
        } finally {
    
    
            try {
    
    
                if (os != null) {
    
    
                    os.close ();
                }
                process.destroy ();
            } catch (Exception e) {
    
    
                e.printStackTrace ();
            }
        }

    }

2.检测Busybox是否存在

Busybox集成了一些Android系统为了安全考虑去掉的一些风险命令,一般root过的手机都有可能安装Busybox

 public static synchronized boolean isDeviceRooted04() {
    
    
        try {
    
    
            Log.i (LOG_TAG, "to exec busybox df");
            String[] strCmd = new String[]{
    
    "busybox", "df"};
            ArrayList<String> execResult = executeCommand (strCmd);
            if (execResult != null) {
    
    
                Log.i (LOG_TAG, "execResult=" + execResult.toString ());
                return true;
            } else {
    
    
                Log.i (LOG_TAG, "execResult=null");
                return false;
            }
        } catch (Exception e) {
    
    
            Log.i (LOG_TAG, "Unexpected error - Here is what I know: "
                    + e.getMessage ());
            return false;
        }

    }

3.访问/data目录查看读写权限

 //写文件
    public static Boolean writeFile(String fileName, String message) {
    
    
        try {
    
    
            FileOutputStream fout = new FileOutputStream (fileName);
            byte[] bytes = message.getBytes ();
            fout.write (bytes);
            fout.close ();
            return true;
        } catch (Exception e) {
    
    
            e.printStackTrace ();
            return false;
        }
    }

    //读文件
    public static String readFile(String fileName) {
    
    
        File file = new File (fileName);
        try {
    
    
            FileInputStream fis = new FileInputStream (file);
            byte[] bytes = new byte[1024];
            ByteArrayOutputStream bos = new ByteArrayOutputStream ();
            int len;
            while ((len = fis.read (bytes)) > 0) {
    
    
                bos.write (bytes, 0, len);
            }
            String result = new String (bos.toByteArray ());
            Log.i (LOG_TAG, result);
            return result;
        } catch (Exception e) {
    
    
            e.printStackTrace ();
            return null;
        }
    }

 public static synchronized boolean isDeviceRooted05() {
    
    
        try {
    
    
            Log.i (LOG_TAG, "to write /data");
            String fileContent = "test_ok";
            Boolean writeFlag = writeFile ("/data/su_test", fileContent);
            if (writeFlag) {
    
    
                Log.i (LOG_TAG, "write ok");
            } else {
    
    
                Log.i (LOG_TAG, "write failed");
            }

            Log.i (LOG_TAG, "to read /data");
            String strRead = readFile ("/data/su_test");
            Log.i (LOG_TAG, "strRead=" + strRead);
            if (fileContent.equals (strRead)) {
    
    
                return true;
            } else {
    
    
                return false;
            }
        } catch (Exception e) {
    
    
            Log.i (LOG_TAG, "Unexpected error - Here is what I know: "
                    + e.getMessage ());
            return false;
        }
    }

4.检测系统是否为测试版

一般不完全root的设备(通过找到系统漏洞提权的设备),会显示不是正版因此可以通过检测版本

 public static synchronized boolean isDeviceRooted06(){
    
    
    String buildTags = android.os.Build.TAGS;
        if(buildTags !=null&&buildTags.contains("test-keys"))

    {
    
    
        Log.i (LOG_TAG, "buildTags=" + buildTags);
        return true;
    }
        return false;
}

5.检查是否存在Superuser.apk

Superuser.apk被广泛用来root安卓设备,我们可以通过检测其是否存在达到检测是否root的目的

 public static synchronized boolean isDeviceRooted07(){
    
    

            try {
    
    
                File file = new File("/system/app/Superuser.apk");
                if (file.exists()) {
    
    
                    Log.i(LOG_TAG,"/system/app/Superuser.apk exist");
                    return true;
                }
            } catch (Exception e) {
    
     }
            return false;

    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43632667/article/details/105778663