АНДРОИД: Как получить root-доступ в приложении Android?
Я разрабатываю свой первый Android
приложение, и мне любопытно, есть ли какие-либо "стандартные" способы выполнения привилегированных shell
команды. Я только смог найти один способ сделать это, выполнив su
и затем добавив мои команды stdin
из su
процесс.
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";
Я читал об упаковке привилегированных (superuser
) звонит в JNI
: Это возможно? Если так, то как можно это сделать? Кроме этого, есть ли другие способы вызова привилегированных инструкций из Java
?
3 ответа
Насколько я знаю, вы можете запускать команды командной строки только с правами суперпользователя. Вы можете использовать этот общий класс, который я создал, который оборачивает корневой доступ в вашем коде: http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html
Все, что вам нужно сделать, это расширить этот класс и переопределить getCommandsToExecute
метод для возврата команд, которые вы хотите выполнить от имени пользователя 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();
}
Насколько мне известно, возможное решение состоит в том, чтобы подписать ваше приложение как системное, которое отличается от root, как я знаю: как подписать приложение Android системной подписью?, Но я полагаю, это не то, что вы хотели.
Еще одна вещь, которую я сделал, - это создать собственное приложение, которое делает то, что нужно, и запускать его как внешний процесс. Но необходимо дать этому нативному приложению необходимые привилегии и бит suid, если раздел не является nosuid. Но это тоже не то, что тебе нужно.
Я полагаю, что код C, вызываемый через JNI, должен подвергаться тем же ограничениям, что и жизнь в одном и том же процессе.
Если у вас есть доступный бинарный файл su, вы можете запускать команды из java примерно так: Runtime.getRuntime().exec("su -c reboot")
,
Я не помню другого пути.
Недавно мне нужно было сделать это на корневом устройстве, и я узнал о libsu, библиотеке, позволяющей выполнять команды su.
Эта библиотека написана автором Magisk.
У меня есть рутованное устройство с Magisk, и libsu работает довольно хорошо из моего приложения. При первом выполнении команды su Magisk покажет диалоговое окно, в котором вы предоставляете или запрещаете права суперпользователя.
Запустить команду с libsu из приложения так же просто, как сделать следующее:
Shell.cmd("find /dev/block -iname boot").exec()