Determinar si se ejecuta en un dispositivo enraizado

Mi aplicación tiene una cierta funcionalidad que sólo funcionará en un dispositivo en el que root esté disponible. En lugar de tener esta función falla cuando se utiliza (y luego mostrar un mensaje de error adecuado para el usuario), prefiero una capacidad de comprobar en silencio si root está disponible en primer lugar, y si no, ocultar las opciones respectivas en el primer lugar .

¿Hay alguna forma de hacer esto?

Aquí hay una clase que comprobará la raíz de una de tres maneras.

/** @author Kevin Kowalewski */ public class RootUtil { public static boolean isDeviceRooted() { return checkRootMethod1() || checkRootMethod2() || checkRootMethod3(); } private static boolean checkRootMethod1() { String buildTags = android.os.Build.TAGS; return buildTags != null && buildTags.contains("test-keys"); } private static boolean checkRootMethod2() { String[] paths = { "/system/app/Superuser.apk", "/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", "/su/bin/su"}; for (String path : paths) { if (new File(path).exists()) return true; } return false; } private static boolean checkRootMethod3() { Process process = null; try { process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" }); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); if (in.readLine() != null) return true; return false; } catch (Throwable t) { return false; } finally { if (process != null) process.destroy(); } } } 

La biblioteca de RootTools ofrece métodos sencillos para comprobar la raíz:

 RootTools.isRootAvailable() 

https://github.com/Stericson/RootTools

En mi aplicación estaba comprobando si el dispositivo está enraizado o no ejecutando el comando "su". Pero hoy he eliminado esta parte de mi código. ¿Por qué?

Porque mi aplicación se convirtió en un asesino de la memoria. ¿Cómo? Déjame contarte mi historia.

Hubo algunas quejas de que mi aplicación estaba ralentizando los dispositivos (Por supuesto, pensé que no puede ser cierto). Traté de averiguar por qué. Así que he utilizado MAT para obtener vertederos de montón y analizar, y todo parecía perfecto. Pero después de relanzar mi aplicación muchas veces me di cuenta de que el dispositivo es realmente más lento y detener mi aplicación no hacerlo más rápido (a menos que reinicie el dispositivo). Analizé los archivos de volcado de nuevo mientras el dispositivo es muy lento. Pero todo era todavía perfecto para el archivo de volcado. Entonces hice lo que debía hacer al principio. Enumeré los procesos.

 $ adb shell ps 

Surprize; Había muchos procesos para mi aplicación (con la etiqueta de proceso de mi aplicación en el manifiesto). Algunos de ellos eran zombies, algunos de ellos no.

Con una aplicación de ejemplo que tiene una sola actividad y ejecuta sólo el comando "su", me di cuenta de que se está creando un proceso zombie en cada lanzamiento de la aplicación. Al principio estos zombis asignan 0KB pero de algo sucede y los procesos zombies sostienen casi los mismos KBs que el proceso principal de mi aplicación y se convirtieron en procesos estándar.

Hay un informe de error para el mismo problema en bugs.sun.com: http://bugs.sun.com/view_bug.do?bug_id=6474073 esto explica si el comando no se encuentra zombies se van a crear con el método exec () . Pero todavía no entiendo por qué y cómo pueden convertirse en procesos estándar y mantener importantes KBs. (Esto no está sucediendo todo el tiempo)

Usted puede intentar si usted quiere con el ejemplo de código abajo;

 String commandToExecute = "su"; executeShellCommand(commandToExecute); 

Simple método de ejecución de comandos;

 private boolean executeShellCommand(String command){ Process process = null; try{ process = Runtime.getRuntime().exec(command); return true; } catch (Exception e) { return false; } finally{ if(process != null){ try{ process.destroy(); }catch (Exception e) { } } } } 

Para resumir; No tengo ningún consejo para determinar si el dispositivo está enraizado o no. Pero si yo fuera tú no usaría Runtime.getRuntime (). Exec ().

Por cierto; RootTools.isRootAvailable () causa el mismo problema.

Muchas de las respuestas enumeradas aquí tienen problemas inherentes:

  • La comprobación de las claves de prueba se correlaciona con el acceso de root, pero no garantiza necesariamente que
  • Los directorios "PATH" deben derivarse de la variable de entorno "PATH" en lugar de ser codificados
  • La existencia del ejecutable "su" no significa necesariamente que el dispositivo ha sido arraigado
  • El "cual" ejecutable puede o no estar instalado, y debe permitir que el sistema resuelva su ruta si es posible
  • El hecho de que la aplicación SuperUser esté instalada en el dispositivo no significa que el dispositivo tenga acceso root aún

La biblioteca RootTools de Stericson parece estar comprobando la raíz más legítimamente. También tiene un montón de herramientas adicionales y utilidades, así que lo recomiendo. Sin embargo, no hay ninguna explicación de cómo se comprueba específicamente la raíz, y puede ser un poco más pesado que la mayoría de las aplicaciones realmente necesitan.

He hecho un par de métodos de utilidad que se basan libremente en la biblioteca de RootTools. Si simplemente desea comprobar si el ejecutable "su" está en el dispositivo, puede utilizar el siguiente método:

 public static boolean isRootAvailable(){ for(String pathDir : System.getenv("PATH").split(":")){ if(new File(pathDir, "su").exists()) { return true; } } return false; } 

Este método simplemente recorre los directorios listados en la variable de entorno "PATH" y comprueba si existe un archivo "su" en uno de ellos.

Con el fin de verificar realmente el acceso root, el comando "su" debe ejecutarse realmente. Si se instala una aplicación como SuperUser, entonces puede solicitar acceso root, o si se le ha concedido o denegado un brindis puede indicar si se ha concedido o denegado el acceso. Un buen comando para ejecutar es "id" para que pueda verificar que el ID de usuario es de hecho 0 (root).

A continuación, se muestra un método de ejemplo para determinar si se ha concedido el acceso root:

 public static boolean isRootGiven(){ if (isRootAvailable()) { Process process = null; try { process = Runtime.getRuntime().exec(new String[]{"su", "-c", "id"}); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String output = in.readLine(); if (output != null && output.toLowerCase().contains("uid=0")) return true; } catch (Exception e) { e.printStackTrace(); } finally { if (process != null) process.destroy(); } } return false; } 

Es importante probar la ejecución del comando "su" porque algunos emuladores tienen el ejecutable "su" preinstalado, pero sólo permiten que ciertos usuarios lo accedan como el shell de adb.

También es importante comprobar la existencia del ejecutable "su" antes de intentar ejecutarlo, porque se ha sabido que android no dispone correctamente de los procesos que intentan ejecutar comandos perdidos. Estos procesos fantasma pueden aumentar el consumo de memoria con el tiempo.

http://code.google.com/p/roottools/

Si no desea utilizar el archivo jar simplemente use el código:

 public static boolean findBinary(String binaryName) { boolean found = false; if (!found) { String[] places = { "/sbin/", "/system/bin/", "/system/xbin/", "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/" }; for (String where : places) { if (new File(where + binaryName).exists()) { found = true; break; } } } return found; } 

El programa intentará encontrar su carpeta:

 private static boolean isRooted() { return findBinary("su"); } 

Ejemplo:

 if (isRooted() == true ){ textView.setText("Device Rooted"); } else{ textView.setText("Device Unrooted"); } 

La comprobación de raíz a nivel Java no es una solución segura. Si su aplicación tiene preocupaciones de seguridad para ejecutarse en un dispositivo enraizado, utilice esta solución.

La respuesta de Kevin funciona a menos que el teléfono también tenga una aplicación como RootCloak. Tales aplicaciones tienen un identificador Java APIs una vez que el teléfono está enraizado y se burlan de estas API para devolver el teléfono no está enraizada.

He escrito un código de nivel nativo basado en la respuesta de Kevin, funciona incluso con RootCloak! También no causa ningún problema de pérdida de memoria.

 #include <string.h> #include <jni.h> #include <time.h> #include <sys/stat.h> #include <stdio.h> #include "android_log.h" #include <errno.h> #include <unistd.h> #include <sys/system_properties.h> JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod1( JNIEnv* env, jobject thiz) { //Access function checks whether a particular file can be accessed int result = access("/system/app/Superuser.apk",F_OK); ANDROID_LOGV( "File Access Result %d\n", result); int len; char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>. len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id). if(strcmp(build_tags,"test-keys") == 0){ ANDROID_LOGV( "Device has test keys\n", build_tags); result = 0; } ANDROID_LOGV( "File Access Result %s\n", build_tags); return result; } JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod2( JNIEnv* env, jobject thiz) { //which command is enabled only after Busy box is installed on a rooted device //Outpput of which command is the path to su file. On a non rooted device , we will get a null/ empty path //char* cmd = const_cast<char *>"which su"; FILE* pipe = popen("which su", "r"); if (!pipe) return -1; char buffer[128]; std::string resultCmd = ""; while(!feof(pipe)) { if(fgets(buffer, 128, pipe) != NULL) resultCmd += buffer; } pclose(pipe); const char *cstr = resultCmd.c_str(); int result = -1; if(cstr == NULL || (strlen(cstr) == 0)){ ANDROID_LOGV( "Result of Which command is Null"); }else{ result = 0; ANDROID_LOGV( "Result of Which command %s\n", cstr); } return result; } JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod3( JNIEnv* env, jobject thiz) { int len; char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>. int result = -1; len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id). if(len >0 && strstr(build_tags,"test-keys") != NULL){ ANDROID_LOGV( "Device has test keys\n", build_tags); result = 0; } return result; } 

En su código Java, debe crear la clase wrapper RootUtils para realizar las llamadas nativas

  public boolean checkRooted() { if( rootUtils.checkRootAccessMethod3() == 0 || rootUtils.checkRootAccessMethod1() == 0 || rootUtils.checkRootAccessMethod2() == 0 ) return true; return false; } 

En lugar de utilizar isRootAvailable () puede utilizar isAccessGiven (). Directo desde RootTools wiki :

 if (RootTools.isAccessGiven()) { // your app has been granted root access } 

RootTools.isAccessGiven () no sólo comprueba que un dispositivo está enraizado, sino que también llama a su para su aplicación, solicita permiso y devuelve true si su aplicación obtuvo permisos de root correctamente. Esto se puede utilizar como la primera comprobación en su aplicación para asegurarse de que se le dará acceso cuando lo necesite.

https://code.google.com/p/roottools/

Algunas modificaciones se utilizan para establecer la propiedad del sistema ro.modversion para este propósito. Las cosas parecen haberse movido; Mi construcción de TheDude hace unos meses tiene esto:

 cmb@apollo:~$ adb -d shell getprop |grep build [ro.build.id]: [CUPCAKE] [ro.build.display.id]: [htc_dream-eng 1.5 CUPCAKE eng.TheDudeAbides.20090427.235325 test-keys] [ro.build.version.incremental]: [eng.TheDude.2009027.235325] [ro.build.version.sdk]: [3] [ro.build.version.release]: [1.5] [ro.build.date]: [Mon Apr 20 01:42:32 CDT 2009] [ro.build.date.utc]: [1240209752] [ro.build.type]: [eng] [ro.build.user]: [TheDude] [ro.build.host]: [ender] [ro.build.tags]: [test-keys] [ro.build.product]: [dream] [ro.build.description]: [kila-user 1.1 PLAT-RC33 126986 ota-rel-keys,release-keys] [ro.build.fingerprint]: [tmobile/kila/dream/trout:1.1/PLAT-RC33/126986:user/ota-rel-keys,release-keys] [ro.build.changelist]: [17615# end build properties] 

El emulador del SDK 1.5, por otro lado, ejecutar la imagen de 1,5, también tiene raíz, es probablemente similar al Android Dev Phone 1 (que presumiblemente desea permitir) y tiene esto:

 cmb@apollo:~$ adb -e shell getprop |grep build [ro.build.id]: [CUPCAKE] [ro.build.display.id]: [sdk-eng 1.5 CUPCAKE 148875 test-keys] [ro.build.version.incremental]: [148875] [ro.build.version.sdk]: [3] [ro.build.version.release]: [1.5] [ro.build.date]: [Thu May 14 18:09:10 PDT 2009] [ro.build.date.utc]: [1242349750] [ro.build.type]: [eng] [ro.build.user]: [android-build] [ro.build.host]: [undroid16.mtv.corp.google.com] [ro.build.tags]: [test-keys] [ro.build.product]: [generic] [ro.build.description]: [sdk-eng 1.5 CUPCAKE 148875 test-keys] [ro.build.fingerprint]: [generic/sdk/generic/:1.5/CUPCAKE/148875:eng/test-keys] 

En cuanto a las construcciones al por menor, no tengo una a mano, pero varias búsquedas en el site:xda-developers.com son informativos. Aquí está un G1 en los Países Bajos , puedes ver que ro.build.tags no tiene test-keys , y pienso que ésa es probablemente la característica más confiable a utilizar.

Si ya está utilizando Fabric / Crashlytics, llame al

 CommonUtils.isRooted(this) 

Esta es la implementación actual de ese método:

 public static boolean isRooted(Context context) { boolean isEmulator = isEmulator(context); String buildTags = Build.TAGS; if(!isEmulator && buildTags != null && buildTags.contains("test-keys")) { return true; } else { File file = new File("/system/app/Superuser.apk"); if(file.exists()) { return true; } else { file = new File("/system/xbin/su"); return !isEmulator && file.exists(); } } } 

Aquí está mi código basado en algunas respuestas aquí:

  /** * Checks if the phone is rooted. * * @return <code>true</code> if the phone is rooted, <code>false</code> * otherwise. */ public static boolean isPhoneRooted() { // get from build info String buildTags = android.os.Build.TAGS; if (buildTags != null && buildTags.contains("test-keys")) { return true; } // check if /system/app/Superuser.apk is present try { File file = new File("/system/app/Superuser.apk"); if (file.exists()) { return true; } } catch (Throwable e1) { // ignore } return false; } 

Más allá de la respuesta de @Kevins, recientemente he encontrado mientras usaba su sistema, que el Nexus 7.1 estaba devolviendo false para los tres métodos – Ningún comando, ninguna test-keys y SuperSU no estaba instalado en /system/app .

He añadido esto:

 public static boolean checkRootMethod4(Context context) { return isPackageInstalled("eu.chainfire.supersu", context); } private static boolean isPackageInstalled(String packagename, Context context) { PackageManager pm = context.getPackageManager(); try { pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES); return true; } catch (NameNotFoundException e) { return false; } } 

Esto es un poco menos útil en algunas situaciones (si necesita un acceso root garantizado), ya que es completamente posible que SuperSU se instale en dispositivos que no tienen acceso a la red.

Sin embargo, dado que es posible tener SuperSU instalado y funcionando, pero no en el directorio /system/app , este caso extra será root (haha) en tales casos.

RootBeer es una raíz que comprueba la biblioteca de Android por Scott y Matthew. Utiliza varias comprobaciones para indicar si el dispositivo está enraizado o no.

Cheques de Java

  • CheckRootManagementApps

  • CheckPotentiallyDangerousAppss

  • CheckRootCloakingApps

  • Comprobar las claves

  • CheckForDangerousProps

  • CheckForBusyBoxBinary

  • CheckForSuBinary

  • CheckSuExists

  • CheckForRWSystem

Cheques nativos

Llamamos a nuestro comprobador de raíz nativo para ejecutar algunos de sus propios cheques. Los controles nativos suelen ser más difíciles de ocultar, por lo que algunas aplicaciones de capa de raíz simplemente bloquean la carga de bibliotecas nativas que contienen ciertas palabras clave.

  • CheckForSuBinary

Dos ideas adicionales, si quieres comprobar si un dispositivo es capaz de root desde tu aplicación:

  1. Compruebe la existencia del binario 'su': ejecute "que su" desde Runtime.getRuntime().exec()
  2. Busque el SuperUser.apk en la ubicación /system/app/Superuser.apk
  public static boolean isRootAvailable(){ Process p = null; try{ p = Runtime.getRuntime().exec(new String[] {"su"}); writeCommandToConsole(p,"exit 0"); int result = p.waitFor(); if(result != 0) throw new Exception("Root check result with exit command " + result); return true; } catch (IOException e) { Log.e(LOG_TAG, "Su executable is not available ", e); } catch (Exception e) { Log.e(LOG_TAG, "Root is unavailable ", e); }finally { if(p != null) p.destroy(); } return false; } private static String writeCommandToConsole(Process proc, String command, boolean ignoreError) throws Exception{ byte[] tmpArray = new byte[1024]; proc.getOutputStream().write((command + "\n").getBytes()); proc.getOutputStream().flush(); int bytesRead = 0; if(proc.getErrorStream().available() > 0){ if((bytesRead = proc.getErrorStream().read(tmpArray)) > 1){ Log.e(LOG_TAG,new String(tmpArray,0,bytesRead)); if(!ignoreError) throw new Exception(new String(tmpArray,0,bytesRead)); } } if(proc.getInputStream().available() > 0){ bytesRead = proc.getInputStream().read(tmpArray); Log.i(LOG_TAG, new String(tmpArray,0,bytesRead)); } return new String(tmpArray); } 

Actualización 2017

Puede hacerlo ahora con la API de Google Safetynet . La API de SafetyNet proporciona API de certificación que le ayuda a evaluar la seguridad y la compatibilidad de los entornos de Android en los que se ejecutan sus aplicaciones.

Esta certificación puede ayudar a determinar si el dispositivo en particular ha sido manipulado o modificado de otra manera.

La Attestation API devuelve una respuesta JWS como ésta

 { "nonce": "R2Rra24fVm5xa2Mg", "timestampMs": 9860437986543, "apkPackageName": "com.package.name.of.requesting.app", "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the certificate used to sign requesting app"], "apkDigestSha256": "base64 encoded, SHA-256 hash of the app's APK", "ctsProfileMatch": true, "basicIntegrity": true, } 

El análisis de esta respuesta puede ayudarlo a determinar si el dispositivo está enraizado o no

Los dispositivos enraizados parecen causar ctsProfileMatch = false.

Usted puede hacerlo en el lado del cliente, pero la respuesta de análisis en el lado del servidor es recomendable. Un archtecture básico del servidor del cliente con el API de la red de la seguridad parecerá esto: –

Introduzca aquí la descripción de la imagen

De hecho es cuestión interesante y hasta ahora nadie ha merecido premio. Utilizo el código siguiente:

  boolean isRooted() { try { ServerSocket ss = new ServerSocket(81); ss.close(); return true; } catch (Exception e) { // not sure } return false; } 

El código no es ciertamente a prueba de balas, porque la red no puede estar disponible por lo que obtendrá una excepción. Si este método devuelve verdadero entonces 99% puede estar seguro, de lo contrario sólo el 50% que no. El permiso de red también puede estropear la solución.

El uso de C ++ con el ndk es el mejor enfoque para detectar raíz, incluso si el usuario está utilizando aplicaciones que ocultan su raíz, como RootCloak. He probado este código con RootCloak y pude detectar la raíz, incluso si el usuario está tratando de ocultarlo. Así que su archivo cpp le gustaría:

 #include <jni.h> #include <string> /** * * function that checks for the su binary files and operates even if * root cloak is installed * @return integer 1: device is rooted, 0: device is not *rooted */ extern "C" JNIEXPORT int JNICALL Java_com_example_user_root_1native_rootFunction(JNIEnv *env,jobject thiz){ const char *paths[] ={"/system/app/Superuser.apk", "/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", "/su/bin/su"}; int counter =0; while (counter<9){ if(FILE *file = fopen(paths[counter],"r")){ fclose(file); return 1; } counter++; } return 0; } 

Y usted llamará a la función de su código java de la siguiente manera

 public class Root_detect { /** * * function that calls a native function to check if the device is *rooted or not * @return boolean: true if the device is rooted, false if the *device is not rooted */ public boolean check_rooted(){ int checker = rootFunction(); if(checker==1){ return true; }else { return false; } } static { System.loadLibrary("cpp-root-lib");//name of your cpp file } public native int rootFunction(); } 
 if [[ "`adb shell which su | grep -io "permission denied"`" != "permission denied" ]]; then echo "Yes. Rooted device." else echo "No. Device not rooted. Only limited tasks can be performed. Done." zenity --warning --title="Device Not Rooted" --text="The connected Android Device is <b>NOT ROOTED</b>. Only limited tasks can be performed." --no-wrap fi 

Usando mi biblioteca en rootbox , es bastante fácil. Compruebe el código requerido a continuación:

  //Pass true to <Shell>.start(...) call to run as superuser Shell shell = null; try { shell = Shell.start(true); } catch (IOException exception) { exception.printStackTrace(); } if (shell == null) // We failed to execute su binary return; if (shell.isRoot()) { // Verified running as uid 0 (root), can continue with commands ... } else throw Exception("Unable to gain root access. Make sure you pressed Allow/Grant in superuser prompt."); 
  • Obtener permiso "root" para la aplicación de Android
  • ¿Cómo funciona su trabajo en android? ¿Y cuáles son los requisitos previos para que funcione? ¿Qué es el enraizamiento en Android?
  • Escribir una aplicación para Android que ejecuta comandos de Linux
  • Habilitar depuración USB a través de Clockworkmod con adb
  • Permiso de READ_LOGS en Jelly Bean (api 16)
  • Construir / compilar Android desde el origen con privilegios de superusuario
  • ¿Cómo hacer que el dispositivo Android siempre esté en modo despertar?
  • ¿Es seguro deshabilitar com.android.systemui?
  • Ejecute el programa android como root
  • ANDROID: ¿Cómo obtener acceso root en una aplicación de Android?
  • ¿Cómo puede detectar si el dispositivo está enraizado en la aplicación?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.