ANDROID: How to gain root access in an Android application?
Asked Answered
E

3

34

I'm developing my first Android application, and I'm curious if there are any "standard" ways for executing privileged shell commands. I've only been able to find one way to do it, by executing su, and then appending my commands to stdin of the su process.

DataOutputStream pOut = new DataOutputStream(p.getOutputStream());
DataInputStream pIn = new DataInputStream(p.getInputStream());

String rv = "";

// su must exit before its output can be read
pOut.writeBytes(cmd + "\nexit\n");
pOut.flush();

p.waitFor();

while (pIn.available() > 0)
    rv += pIn.readLine() + "\n";

I've read about wrapping privileged (superuser) calls up in JNI: is this possible? If so, how would one go about accomplishing it? Other than that, are there any other ways of calling privileged instructions from Java?

Egon answered 5/2, 2011 at 7:51 Comment(3)
There isn't really any means to elevate privilege of an existing process, only means to launch a new process running as root. For an example where su is passed a command to execute, see #4846741Psychogenesis
@Chris what about launching a new Activity/Task/Android java app/etc?Egon
That would require huge changes to android. You don't normally create processes for android java execution, instead you ask android to ask 'zygote' to fork one off, which it does under the user id of that application. Its simpler to keep the root tasks as native executables or shell scripts doing key things on behalf of the application. Or add them to the system proper as privileged system services. Often the best way is to create a unix group and give it access to a particular capability (say access to a device file) and run a service in that group or pair it with an android permissionPsychogenesis
C
42

As far as I know, you can only run command-line commands using root privileges. You can use this generic class I made that wraps the root access in your code: http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html

All you need to do is extend this class and override the getCommandsToExecute method to return the commands you want to execute as root.

public abstract class ExecuteAsRootBase
{
   public static boolean canRunRootCommands()
   {
      boolean retval = false;
      Process suProcess;

      try
      {
         suProcess = Runtime.getRuntime().exec("su");

         DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
         DataInputStream osRes = new DataInputStream(suProcess.getInputStream());

         if (null != os && null != osRes)
         {
            // Getting the id of the current user to check if this is root
            os.writeBytes("id\n");
            os.flush();

            String currUid = osRes.readLine();
            boolean exitSu = false;
            if (null == currUid)
            {
               retval = false;
               exitSu = false;
               Log.d("ROOT", "Can't get root access or denied by user");
            }
            else if (true == currUid.contains("uid=0"))
            {
               retval = true;
               exitSu = true;
               Log.d("ROOT", "Root access granted");
            }
            else
            {
               retval = false;
               exitSu = true;
               Log.d("ROOT", "Root access rejected: " + currUid);
            }

            if (exitSu)
            {
               os.writeBytes("exit\n");
               os.flush();
            }
         }
      }
      catch (Exception e)
      {
         // Can't get root !
         // Probably broken pipe exception on trying to write to output stream (os) after su failed, meaning that the device is not rooted

         retval = false;
         Log.d("ROOT", "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
      }

      return retval;
   }

   public final boolean execute()
   {
      boolean retval = false;

      try
      {
         ArrayList<String> commands = getCommandsToExecute();
         if (null != commands && commands.size() > 0)
         {
            Process suProcess = Runtime.getRuntime().exec("su");

            DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());

            // Execute commands that require root access
            for (String currCommand : commands)
            {
               os.writeBytes(currCommand + "\n");
               os.flush();
            }

            os.writeBytes("exit\n");
            os.flush();

            try
            {
               int suProcessRetval = suProcess.waitFor();
               if (255 != suProcessRetval)
               {
                  // Root access granted
                  retval = true;
               }
               else
               {
                  // Root access denied
                  retval = false;
               }
            }
            catch (Exception ex)
            {
               Log.e("ROOT", "Error executing root action", ex);
            }
         }
      }
      catch (IOException ex)
      {
         Log.w("ROOT", "Can't get root access", ex);
      }
      catch (SecurityException ex)
      {
         Log.w("ROOT", "Can't get root access", ex);
      }
      catch (Exception ex)
      {
         Log.w("ROOT", "Error executing internal operation", ex);
      }

      return retval;
   }
   protected abstract ArrayList<String> getCommandsToExecute();
}
Categorize answered 18/8, 2011 at 5:26 Comment(9)
there seems to be a css error on your code block. always the last character is at the first position?!Instinct
Thanks for sharing your class. I found it more useful to pass the commands as a parameter e.g. boolean execute(ArrayList<String> commands) {...}Rodrick
I've been looking for this kind of information almost 2 days(only command line under su). THX!Coparcenary
@Categorize Can you explain what does 255 in this line: if (255 != suProcessRetval) means ?Danedanegeld
@Categorize I have root, but get "Can't get root access or denied by user". What can cause that?Perfoliate
@StephanGM most likely the su app is set to deny root access to your appCategorize
@Muzikant: When you say "su app" do you mean superuser.apk? I was trying to use this on a rooted phone w/o any extra applications installed.Perfoliate
I am getting "Caused by: java.io.IOException: Cannot run program "su": error=13, Permission denied". How can I configure my emulator to get this permission?Piecemeal
a perfect class! I just tested it, it works. I was wondering how to read the Standard output of a command?Miltie
S
7

A possible solution I know is to sign your application as system, which is not exactly the same as root as far as I know: How to sign Android app with system signature?. But I suppose this is not what you wanted.

Another thing I did is to create a native application that does what is needed, running it as an external process. But it is necessary to give this native application the privileges you need and the suid bit, provided the partition is not nosuid. But this is not what you needed either I suppose.

C code called through JNI should be subject to the same limitations as living in the same process, I suppose.

If you have the su binary available then you can run commands from java with something like: Runtime.getRuntime().exec("su -c reboot").

I don't remember any other way.

Solarium answered 23/4, 2011 at 9:23 Comment(0)
M
0

I was in the need of doing this in a rooted device recently and found out about libsu, which is a library allowing to execute su commands.

This library is written by the author of Magisk.

I have a rooted device with Magisk and libsu works pretty well from my app. The first time you execute a su command, Magisk will show a dialog where you grant or deny the root capabilities.

Running a command with libsu from an app is as simple as doing the following:

Shell.cmd("find /dev/block -iname boot").exec()
Morphosis answered 24/10, 2022 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.