您的位置:首页 > 移动开发 > Android开发

Android5.1.1源码 - zygote fork出的子进程如何权限降级

2015-11-08 00:00 435 查看
摘要: Android5.1.1源码 - zygote fork出的子进程如何权限降级

Android5.1.1源码 - zygote fork出的子进程如何权限降级

@(Android研究)[Android5.1.1|zygote|fork]

[TOC]

前言

本文公开首发于阿里聚安全博客:https://jaq.alibaba.com/community/index.htm?spm=0.0.0.0.ycEUXK

如果不知道zygote是什么,或者好奇zygote如何启动,可以去看老罗的文章:
Android系统进程Zygote启动过程的源代码分析

所有Android应用进程都是zygote fork出来的,新fork出来的应用进程还保持着root权限,这显然是不被允许的,所以这个fork出来的子进程的权限需要被降级,本文说的就是Android源码在什么地方执行了权限降级的操作。

##执行路径

下面的runSelectLoop方法是类ZygoteInit的成员方法,它在文件"frameworks/base/core/java/com/android/internal/os/ZygoteInit.java"中,下面是它的源码:

/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];

fds.add(sServerSocket.getFileDescriptor());
peers.add(null);

int loopCount = GC_LOOP_COUNT;
while (true) {
int index;

/*
* Call gc() before we block in select().
* It's work that has to be done anyway, and it's better
* to avoid making every child do it.  It will also
* madvise() any free memory as a side-effect.
*
* Don't call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}

try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}

if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDescriptor());
} else {
boolean done;
done = peers.get(index).runOnce();

if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}

zygote会在这个方法中等待客户端通知启动一个新的应用程序,详情可以看前言部分列出的文章。现在我们关心的是**done = peers.get(index).runOnce();**语句,这个语句调用了runOnce方法启动了一个新的应用进程,runOnce方法是ZygoteConnection类的成员方法,下文从runOnce方法开始分析。

ZygoteConnection.runOnce方法在文件"frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java"中,下面是它的源码:

/**
* Reads one start command from the command socket. If successful,
* a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
* exception is thrown in that child while in the parent process,
* the method returns normally. On failure, the child is not
* spawned and messages are printed to the log and stderr. Returns
* a boolean status value indicating whether an end-of-file on the command
* socket has been encountered.
*
* @return false if command socket should continue to be read from, or
* true if an end-of-file has been encountered.
* @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
* method in child process
*/
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;

......

try {
......

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
checkTime(startTime, "zygoteConnection.runOnce: postForkAndSpecialize");
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}

......
}

parsedArgs中保存了要启动的应用的信息,它的类型是Arguments,Arguments是ZygoteConnection的内部类。

runOnce方法中调用了Zygote.forkAndSpecialize方法,这个方法在文件"frameworks/base/core/java/com/android/internal/os/Zygote.java"中,下面是它的源码:

/**
* fork出一个新的VM实例。当前VM的启动选项中必须有-Xzygote标志。
* 注意:新实例有所有的root能力(capabilities)。这个新进程被期望调用capset()。
*
* @param uid 新进程的UNIX uid应当在fork()之后且产生任何线程之前调用setuid()。
* @param gid 新进程的UNIX gid应当在fork()之后且产生任何线程之前调用setgid()。
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
* @param debugFlags bit flags that enable debugging features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
* @param seInfo 可以为null,字符串指定新进程的SELinux信息。
* @param niceName null-ok a string specifying the process name.
* @param fdsToClose an array of ints, holding one or more POSIX
* file descriptor numbers that are to be closed by the child
* (and replaced by /dev/null) after forking.  An integer value
* of -1 in any entry in the array means "ignore this one".
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
* @return 如果这是一个子进程,则返回0;如果这是一个父进程,则返回子进程pid;如果出错则返回-1。
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
String instructionSet, String appDataDir) {
long startTime = SystemClock.elapsedRealtime();
VM_HOOKS.preFork();
checkTime(startTime, "Zygote.preFork");
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
instructionSet, appDataDir);
checkTime(startTime, "Zygote.nativeForkAndSpecialize");
VM_HOOKS.postForkCommon();
checkTime(startTime, "Zygote.postForkCommon");
return pid;
}

在这个方法中调用了nativeForkAndSpecialize方法。

nativeForkAndSpecialize是一个native方法,在native代码中它的函数名是
com_android_internal_os_Zygote_nativeForkAndSpecialize
,这个函数在文件"frameworks/base/core/jni/com_android_internal_os_Zygote.cpp"中,下面是它的源码:

static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint debug_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jstring instructionSet, jstring appDataDir) {
// Grant CAP_WAKE_ALARM to the Bluetooth process.
jlong capabilities = 0;
if (uid == AID_BLUETOOTH) {
capabilities |= (1LL << CAP_WAKE_ALARM);
}

return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
se_name, false, fdsToClose, instructionSet, appDataDir);
}

在这个函数中调用了ForkAndSpecializeCommon函数。

子进程权限降级函数

ForkAndSpecializeCommon函数在文件"frameworks/base/core/jni/com_android_internal_os_Zygote.cpp"中,在这个函数中调用了fork函数,并且fork出的子进程将自身权限降级,下面是它的源码:

// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
jstring instructionSet, jstring dataDir) {
......

pid_t pid = fork();

if (pid == 0) {
// 子进程
gMallocLeakZygoteChild = 1;

// Clean up any descriptors which must be closed immediately
DetachDescriptors(env, fdsToClose);

ckTime(start, "ForkAndSpecializeCommon:Fork and detach");

// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(env);
}

......

SetGids(env, javaGids);

SetRLimits(env, javaRlimits);

......

int rc = setresgid(gid, gid, gid);
if (rc == -1) {
ALOGE("setresgid(%d) failed", gid);
RuntimeAbort(env);
}

rc = setresuid(uid, uid, uid);
if (rc == -1) {
ALOGE("setresuid(%d) failed", uid);
RuntimeAbort(env);
}

......

env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
is_system_server ? NULL : instructionSet);
ckTime(start, "ForkAndSpecializeCommon:PostForkChildHooks returns");
if (env->ExceptionCheck()) {
ALOGE("Error calling post fork hooks.");
RuntimeAbort(env);
}
} else if (pid > 0) {
// the parent process
}
return pid;
}

在这个函数中子进程分别调用了SetGids、SetRLimits、setresgid、setresuid,设置了组ID和用户ID
将自身权限降级
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android5.1.1 zygote fork