This is an old question and asked answered by many but here I go again.. I want to check whether the device is rooted or not. I don't want my application to be installed on rooted devices. Now I went through lot of answers and came to a conclusion that there is no guaranteed way to identify root, correct me if I am wrong. Now my question that can we check the device is genuine or not instead of checking for rooted or nor? Are there any application in which I can test my signed APK for code signing, ssl pinning, code exposure and lot many things?
-
A rooted device has an open bootloader most of the times. also it will have busybox installed 90% of times. But all this aside. Google Play Store just released an option for developers to hide their app for rooted devices. Which sure does not forbid it but just makes it a little bit harder for them to get. Possible duplicate of: https://stackoverflow.com/questions/1101380/determine-if-running-on-a-rooted-device Article for hiding apps in play store: http://www.androidguys.com/2017/05/19/google-makes-it-easier-for-devs-to-hide-apps-from-rooted-phone-owners/ – Nico May 29 '17 at 05:32
-
Check this answer: https://stackoverflow.com/questions/3424195/determining-if-an-android-device-is-rooted-programatically – The Dude May 29 '17 at 05:33
-
1No. What defines a "genuine" device? There's no definition for that. – Gabe Sechan May 29 '17 at 05:33
-
@Nico Of course they can trivially side load it. And I expect that setting to work for about another month before its worked around. – Gabe Sechan May 29 '17 at 05:35
-
In the article I just read there is an API called `SafetyNet API` which seems to prevent sideloading and installing on rooted devices. Maybe that's worth a shot. – Nico May 29 '17 at 05:38
-
@GabeSechan as this document states https://developer.android.com/training/safetynet/index.html – Ganesh Ghodake May 29 '17 at 05:39
-
@GaneshGhodake That doesn't mean a device is "genuine". It means that they paid Google to get access to Google's apps (like maps, play store, etc). A tablet running the AOSP without Google apps is still a real Android device (and the majority of devices in many areas of the world). Also, its fairly easy to get around. – Gabe Sechan May 29 '17 at 05:51
5 Answers
Below is my utility class for Root Checking.here is String Constants that i have used this is working for me.
public static final String[] knownRootAppsPackages = {
"com.noshufou.android.su",
"com.noshufou.android.su.elite",
"eu.chainfire.supersu",
"com.koushikdutta.superuser",
"com.thirdparty.superuser",
"com.yellowes.su",
"com.zachspong.temprootremovejb",
"com.ramdroid.appquarantine",
"eu.chainfire.supersu"
};
public static final String[] knownDangerousAppsPackages = {
"com.koushikdutta.rommanager",
"com.koushikdutta.rommanager.license",
"com.dimonvideo.luckypatcher",
"com.chelpus.lackypatch",
"com.ramdroid.appquarantine",
"com.ramdroid.appquarantinepro"
};
public static final String[] knownRootCloakingPackages = {
"com.devadvance.rootcloak",
"com.devadvance.rootcloakplus",
"de.robv.android.xposed.installer",
"com.saurik.substrate",
"com.zachspong.temprootremovejb",
"com.amphoras.hidemyroot",
"com.amphoras.hidemyrootadfree",
"com.formyhm.hiderootPremium",
"com.formyhm.hideroot"
};
public static final String[] suPaths ={
"/data/local/",
"/data/local/bin/",
"/data/local/xbin/",
"/sbin/",
"/su/bin/",
"/system/bin/",
"/system/bin/.ext/",
"/system/bin/failsafe/",
"/system/sd/xbin/",
"/system/usr/we-need-root/",
"/system/xbin/"
};
public static final String[] pathsThatShouldNotBeWrtiable = {
"/system",
"/system/bin",
"/system/sbin",
"/system/xbin",
"/vendor/bin",
//"/sys",
"/sbin",
"/etc",
//"/proc",
//"/dev"
}
;
public class RootUtil {
public static boolean isDeviceRooted() {
return detectRootManagementApps() || detectPotentiallyDangerousApps() || checkForBinary("su")
|| checkForBinary("busybox") || checkForDangerousProps() || checkForRWPaths()
|| detectTestKeys() || checkSuExists();
}
public static boolean detectTestKeys() {
String buildTags = android.os.Build.TAGS;
String buildFinger= Build.FINGERPRINT;
String product=Build.PRODUCT;
String hardware=Build.HARDWARE;
String display=Build.DISPLAY;
return (buildTags != null) && (buildTags.contains("test-keys")|| buildFinger.contains("genric.*test-keys")||product.contains("generic")||product.contains("sdk")||hardware.contains("goldfish")||display.contains(".*test-keys"));
}
public static boolean detectRootManagementApps() {
return detectRootManagementApps(null);
}
public static boolean detectRootManagementApps(String[] additionalRootManagementApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(Constants.knownRootAppsPackages));
if (additionalRootManagementApps!=null && additionalRootManagementApps.length>0){
packages.addAll(Arrays.asList(additionalRootManagementApps));
}
return isAnyPackageFromListInstalled(packages);
}
public static boolean detectPotentiallyDangerousApps() {
return detectPotentiallyDangerousApps(null);
}
public static boolean detectPotentiallyDangerousApps(String[] additionalDangerousApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(Constants.knownDangerousAppsPackages));
if (additionalDangerousApps!=null && additionalDangerousApps.length>0){
packages.addAll(Arrays.asList(additionalDangerousApps));
}
return isAnyPackageFromListInstalled(packages);
}
public boolean detectRootCloakingApps() {
return detectRootCloakingApps(null);
}
public boolean detectRootCloakingApps(String[] additionalRootCloakingApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(Constants.knownRootCloakingPackages));
if (additionalRootCloakingApps!=null && additionalRootCloakingApps.length>0){
packages.addAll(Arrays.asList(additionalRootCloakingApps));
}
return isAnyPackageFromListInstalled(packages);
}
public boolean checkForSuBinary(){
return checkForBinary("su");
}
public boolean checkForBusyBoxBinary(){
return checkForBinary("busybox");
}
public static boolean checkForBinary(String filename) {
String[] pathsArray = Constants.suPaths;
boolean result = false;
for (String path : pathsArray) {
String completePath = path + filename;
File f = new File(completePath);
boolean fileExists = f.exists();
if (fileExists) {
result = true;
}
}
return result;
}
private static String[] propsReader() {
InputStream inputstream = null;
try {
inputstream = Runtime.getRuntime().exec("getprop").getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
String propval = "";
try {
propval = new Scanner(inputstream).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
}
return propval.split("\n");
}
private static String[] mountReader() {
InputStream inputstream = null;
try {
inputstream = Runtime.getRuntime().exec("mount").getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
if (inputstream == null) return null;
String propval = "";
try {
propval = new Scanner(inputstream).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
e.printStackTrace();
}
return propval.split("\n");
}
private static boolean isAnyPackageFromListInstalled(List<String> packages){
boolean result = false;
PackageManager pm = MobileTechnicianApp.getAppContext().getPackageManager();
for (String packageName : packages) {
try {
pm.getPackageInfo(packageName, 0);
result = true;
} catch (PackageManager.NameNotFoundException e) {
}
}
return result;
}
public static boolean checkForDangerousProps() {
final Map<String, String> dangerousProps = new HashMap<>();
dangerousProps.put("ro.debuggable", "1");
dangerousProps.put("ro.secure", "0");
boolean result = false;
String[] lines = propsReader();
for (String line : lines) {
for (String key : dangerousProps.keySet()) {
if (line.contains(key)) {
String badValue = dangerousProps.get(key);
badValue = "[" + badValue + "]";
if (line.contains(badValue)) {
result = true;
}
}
}
}
return result;
}
public static boolean checkForRWPaths() {
boolean result = false;
String[] lines = mountReader();
for (String line : lines) {
String[] args = line.split(" ");
if (args.length < 4){
continue;
}
String mountPoint = args[1];
String mountOptions = args[3];
for(String pathToCheck: Constants.pathsThatShouldNotBeWrtiable) {
if (mountPoint.equalsIgnoreCase(pathToCheck)) {
for (String option : mountOptions.split(",")){
if (option.equalsIgnoreCase("rw")){
result = true;
break;
}
}
}
}
}
return result;
}
public static boolean checkSuExists() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[] { "which", "su" });
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
return in.readLine() != null;
} catch (Throwable t) {
return false;
} finally {
if (process != null) process.destroy();
}
}
}

- 867
- 1
- 12
- 20
-
-
based on current android code need to replace: if (args.length < 6) { continue; } String mountPoint = args[2]; String mountOptions = args[5]; – Tapa Save May 14 '23 at 06:44
-
and next: for (String option : mountOptions.replaceAll("\\(", "").replaceAll("\\)", "").split(",")){ – Tapa Save May 14 '23 at 06:45
It's possible to check root with firebase-crashlytics
. Static method from com.google.firebase.crashlytics.internal.common.CommonUtils.isRooted(Context)
check root but also it's check isEmulator

- 6,056
- 2
- 34
- 46
Check this link: Determine if running on a rooted device
Found some methods to check that device is root or not. It is working fine.
DemoActivity.class
if (isDeviceRooted()) {
DialogFactory.getInstance().showAlertDialog(this, null, 0, "This application can't run on Rooted android phone", "Exit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finishAffinity();
}
}, false);
}
else {
/* Do your code */
}
public static boolean isDeviceRooted() {
return Utils.checkRootMethod1() || Utils.checkRootMethod2() || Utils.checkRootMethod3();
}
Utils.class
public static boolean checkRootMethod1() {
String buildTags = android.os.Build.TAGS;
return buildTags != null && buildTags.contains("test-keys");
}
public 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;
}
public 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()));
return in.readLine() != null;
} catch (Throwable t) {
return false;
} finally {
if (process != null) process.destroy();
}
}

- 2,139
- 19
- 15
-
These methods seem to be identical to this answer back in 2011 https://stackoverflow.com/a/8097801/279320. – Yohan Liyanage Aug 19 '17 at 16:55
Using google SafetyNet Attestation API you can easily check that if the device is genuine or not.
Add dependency in build.gradle(:app)
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'
Get Api key and enable Android Device Verification API using link
Add this method into your project and call from anywhere you want to check the values authenticity of device.
public static void sendSafetyNetRequest(Activity context) {
if(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context,
13000000) == ConnectionResult.SUCCESS) {
Log.e(TAG, "The SafetyNet Attestation API is available");
// TODO(developer): Change the nonce generation to include your own, used once value,
// ideally from your remote server.
String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
Random mRandom = new SecureRandom();
byte[] bytes = new byte[24];
mRandom.nextBytes(bytes);
try {
byteStream.write(bytes);
byteStream.write(nonceData.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
byte[] nonce = byteStream.toByteArray();
SafetyNetClient client = SafetyNet.getClient(context);
Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, API_KEY_FROM_STEP_2_LINK);
task.addOnSuccessListener(context, attestationResponse -> {
/*
TODO(developer): Forward this result to your server together with
the nonce for verification.
You can also parse the JwsResult locally to confirm that the API
returned a response by checking for an 'error' field first and before
retrying the request with an exponential backoff.
NOTE: Do NOT rely on a local, client-side only check for security, you
must verify the response on a remote server!
*/
String jwsResult = attestationResponse.getJwsResult();
Log.e(TAG, "Success! SafetyNet result:\n" + jwsResult + "\n");
if (jwsResult == null) {
Log.e(TAG, "jwsResult Null");
}
final String[] jwtParts = jwsResult.split("\\.");
if (jwtParts.length == 3) {
String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
Log.e(TAG, "decodedPayload : " + decodedPayload);
}
});
task.addOnFailureListener(context, e -> {
// An error occurred while communicating with the service.
String mResult = null;
if (e instanceof ApiException) {
// An error with the Google Play Services API contains some additional details.
ApiException apiException = (ApiException) e;
Util.showLog(TAG, "Error: " +
CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
apiException.getStatusMessage());
} else {
// A different, unknown type of error occurred.
Log.e(TAG, "ERROR! " + e.getMessage());
}
});
} else {
Log.e(TAG, "Prompt user to update Google Play services.";
}
}
- Check your logs for decodedPayload if ctsProfileMatch and basicIntegrity and check the values in table . The Attestation API returns a JWS response which looks like:
{ "nonce": "6pLrr9zWyl6TNzj+kpbR4LZcfPY3U2FmZXR5IE5ldCBTYW1wbGU6IDE2MTQ2NzkwMTIzNjc=", "timestampMs": 9860437986543, "apkPackageName": " your package name will be displayed here", "ctsProfileMatch": true, "apkDigestSha256": [ "base64 encoded, SHA-256 hash of the certificate used to sign requesting app" ], "basicIntegrity": true, "evaluationType": "BASIC" }
[

- 2,077
- 14
- 32
Implementation Class
if(DeviceUtils.isDeviceRooted()){
showAlertDialogAndExitApp("This device is rooted. You can't use this app.");
}else{
Intent intent = new Intent(SplashScreen.this, MainActivity.class);
intent.putExtra("My_notification","2");
startActivity(intent);
}
showing dialog
public void showAlertDialogAndExitApp(String message) {
AlertDialog alertDialog = new AlertDialog.Builder(SplashScreen.this).create();
alertDialog.setTitle("Alert");
alertDialog.setMessage(message);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
});
alertDialog.show();
}
Root device checking class
public class DeviceUtils {
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" };
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();
}
}
}

- 53
- 7