Android 安全攻防(三): SEAndroid Zygote
在Android系统中,所有的应用程序进程,以及系统服务进程SystemServer都是由Zygote孕育fork出来的。 Zygote的native获取主要研究dalvik/vm/native/dalvik_system_Zygote.cpp,SEAndroid管控应用程序资源存取权限,对于整个dalvik,也正是在此动的手脚。
首先看抛出的DalvikNativeMethod dvm_dalvik_system_Zygote,与原生Android相比,SEAndroid 在 nativeForkAndSpecialize 增加传入了两个String类型的参数:
const DalvikNativeMethod dvm_dalvik_system_Zygote[] = { {"nativeFork", "()I", Dalvik_dalvik_system_Zygote_fork }, { "nativeForkAndSpecialize", "(II[II[[ILjava/lang/String;Ljava/lang/String;)I", Dalvik_dalvik_system_Zygote_forkAndSpecialize }, { "nativeForkSystemServer", "(II[II[[IJJ)I", Dalvik_dalvik_system_Zygote_forkSystemServer }, { "nativeExecShell", "(Ljava/lang/String;)V", Dalvik_dalvik_system_Zygote_execShell }, { NULL, NULL, NULL }, }
/* native public static int forkAndSpecialize(int uid, int gid, * int[] gids, int debugFlags, String seInfo, String niceName); */ static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args, JValue* pResult) { pid_t pid; pid = forkAndSpecializeCommon(args, false); RETURN_INT(pid); }
在static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)中,其中SEAndroid加入了设置SELinux安全上下文代码段,seInfo和niceName:
#ifdef HAVE_SELINUX err = setSELinuxContext(uid, isSystemServer, seInfo, niceName); if (err < 0) { LOGE("cannot set SELinux context: %s\n", strerror(errno)); dvmAbort(); } free(seInfo); free(niceName); #endif
#ifdef HAVE_SELINUX /* * Set SELinux security context. * * Returns 0 on success, -1 on failure. */ static int setSELinuxContext(uid_t uid, bool isSystemServer, const char *seInfo, const char *niceName) { #ifdef HAVE_ANDROID_OS return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName); #else return 0; #endif } #endif
再往上一层就到了libcore/dalvik/src/main/java/dalvik/system/Zygote.java ,Zygote类的封装,对应forkAndSpecialize方法中添加seInfo和niceName参数传递。
public class Zygote { ... public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, String seInfo, String niceName) { preFork(); int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits, seInfo, niceName); postFork(); return pid; } native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, String seInfo, String niceName); /** * Forks a new VM instance. * @deprecated use {@link Zygote#forkAndSpecialize(int, int, int[], int, int[][])} */ @Deprecated public static int forkAndSpecialize(int uid, int gid, int[] gids, boolean enableDebugger, int[][] rlimits) { int debugFlags = enableDebugger ? DEBUG_ENABLE_DEBUGGER : 0; return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits, null, null); } ... }
Android应用程序启动流程不再赘述,当建立了ZygoteConnection对象用于socket连接后,接下来就是调用ZygoteConnection.runOnce函数进一步处理了。
源码位置:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java,其中,SEAndroid增加zygote安全策略函数,在runOnce中调用。
/** * Applies zygote security policy. * Based on the credentials of the process issuing a zygote command: * <ol> * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a * wrapper command. * <li> Any other uid may not specify any invoke-with argument. * </ul> * * @param args non-null; zygote spawner arguments * @param peer non-null; peer credentials * @throws ZygoteSecurityException */ private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException { int peerUid = peer.getUid(); if (args.invokeWith != null && peerUid != 0) { throw new ZygoteSecurityException("Peer is not permitted to specify " + "an explicit invoke-with wrapper command"); } if (args.invokeWith != null) { boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext, peerSecurityContext, "zygote", "specifyinvokewith"); if (!allowed) { throw new ZygoteSecurityException("Peer is not permitted to specify " + "an explicit invoke-with wrapper command"); } } } /** * Applies zygote security policy for SEAndroid information. * * @param args non-null; zygote spawner arguments * @param peer non-null; peer credentials * @throws ZygoteSecurityException */ private static void applyseInfoSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException { int peerUid = peer.getUid(); if (args.seInfo == null) { // nothing to check return; } if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) { // All peers with UID other than root or SYSTEM_UID throw new ZygoteSecurityException( "This UID may not specify SEAndroid info."); } boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext, peerSecurityContext, "zygote", "specifyseinfo"); if (!allowed) { throw new ZygoteSecurityException( "Peer may not specify SEAndroid info"); } return; }
理所当然的,在启动一个新的进程时,frameworks/base/core/java/android/os/Process.java中也会加入SEAndroid信息seInfo。