My app has a certain piece of functionality that will only work on a device where root is available. Rather than having this feature fail when it is used (and then show an a
Indeed it is interesting question and so far nobody has deserved award. I use the following code:
boolean isRooted() {
try {
ServerSocket ss = new ServerSocket(81);
ss.close();
return true;
} catch (Exception e) {
// not sure
}
return false;
}
The code is certainly not bulletproof, because network can be not available so you get an exception. If this method returns true then 99% you can be sure, otherwise just 50% that not. Networking permission can also spoil the solution.
Using C++ with the ndk is the best approach to detect root even if the user is using applications that hide his root such as RootCloak. I tested this code with RootCloak and I was able to detect the root even if the user is trying to hide it. So your cpp file would like:
#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;
}
And you will call the function from your java code as follows
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();
}
Using my library at rootbox, it is pretty easy. Check the required code below:
//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.");
In my application I was checking if device is rooted or not by executing "su" command. But today I've removed this part of my code. Why?
Because my application became a memory killer. How? Let me tell you my story.
There were some complaints that my application was slowing down devices(Of course I thought that can not be true). I tried to figure out why. So I used MAT to get heap dumps and analyze, and everything seemed perfect. But after relaunching my app many times I realized that device is really getting slower and stopping my application didn't make it faster (unless I restart device). I analyzed dump files again while device is very slow. But everything was still perfect for dump file. Then I did what must be done at first. I listed processes.
$ adb shell ps
Surprize; there were many processes for my application (with my application's process tag at manifest). Some of them was zombie some of them not.
With a sample application which has a single Activity and executes just "su" command, I realized that a zombie process is being created on every launch of application. At first these zombies allocate 0KB but than something happens and zombie processes are holding nearly same KBs as my application's main process and they became standart processes.
There is a bug report for same issue on bugs.sun.com: http://bugs.sun.com/view_bug.do?bug_id=6474073 this explains if command is not found zombies are going to be created with exec() method. But I still don't understand why and how can they become standart processes and hold significant KBs. (This is not happening all the time)
You can try if you want with code sample below;
String commandToExecute = "su";
executeShellCommand(commandToExecute);
Simple command execution method;
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) {
}
}
}
}
To sum up; I have no advice for you to determine if device is rooted or not. But if I were you I would not use Runtime.getRuntime().exec().
By the way; RootTools.isRootAvailable() causes same problem.
if you don't want to use any 3rd party library or any random solution then just use google lib for detecting it.
Android Device Verification
response :
{
"timestampMs": 9860437986543,
"nonce": "R2Rra24fVm5xa2Mg",
"apkPackageName": "com.package.name.of.requesting.app",
"apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
certificate used to sign requesting app"],
"ctsProfileMatch": true,
"basicIntegrity": true,
}
ctsProfileMatch it gives false if the device is rooted.
ref link: [1]: https://developer.android.com/training/safetynet/attestation
There is Safety Net Attestation API of Google play services by which we can assess the device and determine if it is rooted/tampered.
Please go through my answer to deal with rooted devices:
https://stackoverflow.com/a/58304556/3908895