博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android5.1.1源码 - zygote fork出的子进程如何权限降级
阅读量:6584 次
发布时间:2019-06-24

本文共 9158 字,大约阅读时间需要 30 分钟。

  hot3.png

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

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


[TOC]


前言

本文公开首发于阿里聚安全博客:

如果不知道zygote是什么,或者好奇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
fds = new ArrayList
(); ArrayList
peers = new ArrayList
(); 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将自身权限降级

转载于:https://my.oschina.net/ibuwai/blog/527801

你可能感兴趣的文章
Mongodb 备份和恢复
查看>>
MySQL日志管理
查看>>
CentOS yum 提示段错误 (core dumped)
查看>>
HTML中各种互联网媒体类型(MIME)汇总
查看>>
Spring-MVC开发之全局异常捕获全面解读
查看>>
linux命令diff
查看>>
JSTL标签库使用
查看>>
SVN小记
查看>>
LAMP - Apache访问控制
查看>>
七日Python之路--第三天(之初试Django 2-2)
查看>>
cannot find a valid baseurl for repo base centos 6
查看>>
邮件服务器持续发展需注重个性化服务
查看>>
我的友情链接
查看>>
java读取Excel文件
查看>>
mac使用笔记
查看>>
日期时间工具类
查看>>
Cisco Packet Tracer 7.0 简单的使用教程
查看>>
GRE 6to4实验
查看>>
C#读写文件:十进制转十六进制
查看>>
GitHub使用
查看>>