在android用管理员权限执行指令很简单,就像这样
public static void sudo(String cmd) {
Process su;
DataOutputStream outputStream = null;
try {
su = Runtime.getRuntime().exec("su");
outputStream = new DataOutputStream(su.getOutputStream());
outputStream.writeBytes(cmd + "\n");
outputStream.flush();
outputStream.writeBytes("exit\n");
outputStream.flush();
su.waitFor();
su.getInputStream().close();
su.getErrorStream().close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} finally {
Closer.closeSilently(outputStream);
}
}
Runtime.exec 做了什么呢?
java.lang.ProcessManager
/** Executes a command in a child process. */
static pid_t ExecuteProcess(JNIEnv* env, char** commands, char** environment,
const char* workingDirectory, jobject inDescriptor,
jobject outDescriptor, jobject errDescriptor,
jboolean redirectErrorStream) {
//... ignore
pid_t childPid = fork();
//... ignore
// Execute process. By convention, the first argument in the arg array
// should be the command itself.
execvp(commands[0], commands);
//... ignore
}
所以是fork出一个进程来执行su的指令,获得root权限是执行su指令之后的事情,su指令fork出一个子进程。
接着看su指令是怎么做的
在android_x86中的目录是:external/koush/Superuser/Superuser/jni/su/su.c
简单介绍下背景好了
android的权限机制其实就是linux权限那一套东西,所以root账号是有的root权限也是有的,不过root权限的uid和gid都是0,android系统在init的时候就是用 0 起的。su指令提供了一个方法,可以切换过去。换句话说,只要能把进程的uid和gid变为 0 那么就获得了ROOT权限。指令是不是叫su其实没关系。
在android_x86中,su 和 superuser 是配套使用的,具体细节如下:
static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
char *arg0;
int argc, err;
umask(ctx->umask);
int send_to_app = 1;
// no need to log if called by root
if (ctx->from.uid == AID_ROOT)
send_to_app = 0;
// dumpstate (which logs to logcat/shell) will spam the crap out of the system with su calls
if (strcmp("/system/bin/dumpstate", ctx->from.bin) == 0)
send_to_app = 0;
if (send_to_app)
send_result(ctx, ALLOW);
char *binary;
argc = ctx->to.optind;
if (ctx->to.command) {
binary = ctx->to.shell;
ctx->to.argv[--argc] = ctx->to.command;
ctx->to.argv[--argc] = "-c";
}
else if (ctx->to.shell) {
binary = ctx->to.shell;
}
else {
if (ctx->to.argv[argc]) {
binary = ctx->to.argv[argc++];
}
else {
binary = DEFAULT_SHELL;
}
}
arg0 = strrchr (binary, '/');
arg0 = (arg0) ? arg0 + 1 : binary;
if (ctx->to.login) {
int s = strlen(arg0) + 2;
char *p = malloc(s);
if (!p)
exit(EXIT_FAILURE);
*p = '-';
strcpy(p + 1, arg0);
arg0 = p;
}
populate_environment(ctx);
set_identity(ctx->to.uid);
#define PARG(arg) \
(argc + (arg) < ctx->to.argc) ? " " : "", \
(argc + (arg) < ctx->to.argc) ? ctx->to.argv[argc + (arg)] : ""
LOGD("%u %s executing %u %s using binary %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
ctx->from.uid, ctx->from.bin,
ctx->to.uid, get_command(&ctx->to), binary,
arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5),
(ctx->to.optind + 6 < ctx->to.argc) ? " ..." : "");
ctx->to.argv[--argc] = arg0;
execvp(binary, ctx->to.argv + argc);
err = errno;
PLOGE("exec");
fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(err));
exit(EXIT_FAILURE);
}
执行是
execvp(binary, ctx->to.argv + argc);
在app里面binary默认就是走的sh(/system/bin/su),也就是,所以就和执行Runtime.getRuntime().exec("sh") 的效果是一致的,除了权限的不同。
sh是怎么做的呢,原理应该是每次监听输入,fork一个进程去执行指令,apue中有提供了一个类似的例子
#include "apue.h"
#include
int
main(int argc, char *argv[])
{
char buf[MAXLINE];
pid_t pid;
int status;
printf("%% ");
while(fgets(buf,MAXLINE,stdin) != NULL){
if(buf[strlen(buf) - 1 ] == '\n')
buf[strlen(buf) - 1 ] = 0;
if((pid = fork()) <0){
err_sys("fork error");
}else if(pid == 0){
execlp(buf,buf,(char *)0);
err_ret("couldn't excute: %s",buf);
exit(127);
}
if((pid = waitpid(pid,&status,0))<0)
err_sys("wait pid error");
printf("%% ");
}
exit(0);
}