Android 安全攻防

SEAndroid(Security-Enhanced Android),是将原本运用在Linux操作系统上的MAC强制存取控管套件SELinux,移植到Android平台上。可以用来强化Android操作系统对App的存取控管,建立类似沙箱的执行隔离效果,来确保每一个App之间的独立运作,也因此可以阻止恶意App对系统或其它应用程序的攻击。

SEAndroid的中心理念是,即使root权限被篡夺,只求阻止应用的恶意行为。

 

bionic 作为第一个分析的模块,在此之前,先简单叙述一下SEAndroid,便于后续理解。

 

SEAndroid 与 Android 的区别

 
一句话概括:SEAndroid = SElinux + Android , 通过MAC的方式管控应用程序,从而提升原生Android系统的安全性。

 

SEAndroid 如何管控程序

一句话概括:程序向SEAndroid(确切的说是SELinux)发送请求,SEAndroid根据策略数据库进行策略分析,比对安全上下文,控制应用程序的资源存取。

 

 

 

SEAndroid  bionic 

SEAndroid在架构和机制上与SELinux完全一样,考虑到移动设备的特点,所以移植到SEAndroid的只是SELinux的一个子集。

1.增加系统调用

对比Android,SEAndroid bionic下增加了12个 system call :

  1. int fsetxattr(int, const char *, const void *, size_t, int)  
  2. ssize_t fgetxattr(int, const char *, void *, size_t)  
  3. ssize_t flistxattr(int, char *, size_t)  
  4. int fremovexattr(int, const char *)  
  5. int setxattr(const char *, const char *, const void *, size_t, int)  
  6. int lsetxattr(const char *, const char *, const void *, size_t, int)  
  7. ssize_t getxattr(const char *, const char *, void *, size_t)  
  8. ssize_t lgetxattr(const char *, const char *, void *, size_t)  
  9. ssize_t listxattr(const char *, char *, size_t)  
  10. ssize_t llistxattr(const char *, char *, size_t)  
  11. int removexattr(const char *, const char *)  
  12. int lremovexattr(const char *, const char *)  

2.linker增加AT_SECURE

SELinux为加载的应用程序设置AT_SECURE  auxv 标志,具体在bionic/linker/linker.c:

  1. /* Initialize environment functions, and get to the ELF aux vectors table */  
  2. vecs = linker_env_init(vecs);  
  3.   
  4.   
  5. /* Check auxv for AT_SECURE first to see if program is setuid, setgid, 
  6. has file caps, or caused a SELinux/AppArmor domain transition. */  
  7. for (v = vecs; v[0]; v += 2) {  
  8.     if (v[0] == AT_SECURE) {  
  9.     /* kernel told us whether to enable secure mode */  
  10.         program_is_setuid = v[1];  
  11.         goto sanitize;  
  12.     }  
  13. }  
  14.   
  15. /* Kernel did not provide AT_SECURE - fall back on legacy test. */  
  16. program_is_setuid = (getuid() != geteuid()) || (getgid() != getegid());  

综上所述,bionic差异并不大,主要是AT_SECURE的加入,  SELinux水很深,推荐鸟哥的文章一读:

鳥哥的 Linux 私房菜-第十七章、程序管理與 SELinux 初探

 

在Android系统中,所有的应用程序进程,以及系统服务进程SystemServer都是由Zygote孕育fork出来的。 Zygote的native获取主要研究dalvik/vm/native/dalvik_system_Zygote.cpp,SEAndroid管控应用程序资源存取权限,对于整个dalvik,也正是在此动的手脚。

首先看抛出的DalvikNativeMethod dvm_dalvik_system_Zygote,与原生Android相比,SEAndroid 在 nativeForkAndSpecialize 增加传入了两个String类型的参数:

  1. const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {  
  2.     {"nativeFork", "()I",  
  3.         Dalvik_dalvik_system_Zygote_fork },  
  4.     { "nativeForkAndSpecialize", "(II[II[[ILjava/lang/String;Ljava/lang/String;)I",  
  5.         Dalvik_dalvik_system_Zygote_forkAndSpecialize },  
  6.     { "nativeForkSystemServer", "(II[II[[IJJ)I",  
  7.         Dalvik_dalvik_system_Zygote_forkSystemServer },  
  8.     { "nativeExecShell", "(Ljava/lang/String;)V",  
  9.         Dalvik_dalvik_system_Zygote_execShell },  
  10.     { NULL, NULL, NULL },  
  11. }  


那么这两个参数是什么呢?继续追一下forkAndSpecialize。

  1. /* native public static int forkAndSpecialize(int uid, int gid, 
  2. * int[] gids, int debugFlags, String seInfo, String niceName); 
  3. */  
  4. static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,  
  5. JValue* pResult)  
  6. {  
  7.     pid_t pid;  
  8.   
  9.     pid = forkAndSpecializeCommon(args, false);  
  10.   
  11.     RETURN_INT(pid);  
  12. }  


可以看到,增加传入的2个参数一个是seInfo,用于定义新进程的SEAndroid信息,一个是niceName,用于定义新进程名。

在static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)中,其中SEAndroid加入了设置SELinux安全上下文代码段,seInfo和niceName:

  1. #ifdef HAVE_SELINUX  
  2.     err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);  
  3.     if (err < 0) {  
  4.         LOGE("cannot set SELinux context: %s\n", strerror(errno));  
  5.         dvmAbort();  
  6.     }  
  7.     free(seInfo);  
  8.     free(niceName);  
  9. #endif  


其中设置SELinux安全上下文方法实现:

  1. #ifdef HAVE_SELINUX  
  2. /* 
  3. * Set SELinux security context. 
  4. * Returns 0 on success, -1 on failure. 
  5. */  
  6. static int setSELinuxContext(uid_t uid, bool isSystemServer,  
  7. const char *seInfo, const char *niceName)  
  8. {  
  9. #ifdef HAVE_ANDROID_OS  
  10.     return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);  
  11. #else  
  12.     return 0;  
  13. #endif  
  14. }  
  15. #endif  

再往上一层就到了libcore/dalvik/src/main/java/dalvik/system/Zygote.java ,Zygote类的封装,对应forkAndSpecialize方法中添加seInfo和niceName参数传递。

  1. public class Zygote {  
  2. ...  
  3.     public static int forkAndSpecialize(int uid, int gid, int[] gids,  
  4.             int debugFlags, int[][] rlimits, String seInfo, String niceName) {  
  5.         preFork();  
  6.         int pid = nativeForkAndSpecialize(uid, gid, gids, debugFlags, rlimits, seInfo, niceName);  
  7.         postFork();  
  8.         return pid;  
  9.     }  
  10.   
  11.   
  12.     native public static int nativeForkAndSpecialize(int uid, int gid,  
  13.             int[] gids, int debugFlags, int[][] rlimits, String seInfo, String niceName);  
  14.   
  15.   
  16.     /** 
  17.      * Forks a new VM instance. 
  18.      * @deprecated use {@link Zygote#forkAndSpecialize(int, int, int[], int, int[][])} 
  19.      */  
  20.     @Deprecated  
  21.     public static int forkAndSpecialize(int uid, int gid, int[] gids,  
  22.             boolean enableDebugger, int[][] rlimits) {  
  23.         int debugFlags = enableDebugger ? DEBUG_ENABLE_DEBUGGER : 0;  
  24.         return forkAndSpecialize(uid, gid, gids, debugFlags, rlimits, null, null);  
  25.     }  
  26. ...  
  27. }  

 

Android应用程序启动流程不再赘述,当建立了ZygoteConnection对象用于socket连接后,接下来就是调用ZygoteConnection.runOnce函数进一步处理了。

源码位置:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java,其中,SEAndroid增加zygote安全策略函数,在runOnce中调用。

  1. /** 
  2.     * Applies zygote security policy. 
  3.     * Based on the credentials of the process issuing a zygote command: 
  4.     * 
       
    1.     * 
    2.  uid 0 (root) may specify --invoke-with to launch Zygote with a 
    3.     * wrapper command. 
    4.     * 
    5.  Any other uid may not specify any invoke-with argument. 
    6.     *  
    7.     * 
    8.     * @param args non-null; zygote spawner arguments 
    9.     * @param peer non-null; peer credentials 
    10.     * @throws ZygoteSecurityException 
    11.     */  
    12.    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer,  
    13.            String peerSecurityContext)  
    14.            throws ZygoteSecurityException {  
    15.        int peerUid = peer.getUid();  
    16.   
    17.        if (args.invokeWith != null && peerUid != 0) {  
    18.            throw new ZygoteSecurityException("Peer is not permitted to specify "  
    19.                    + "an explicit invoke-with wrapper command");  
    20.        }  
    21.   
    22.        if (args.invokeWith != null) {  
    23.            boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,  
    24.                                                         peerSecurityContext,  
    25.                                                         "zygote",  
    26.                                                         "specifyinvokewith");  
    27.            if (!allowed) {  
    28.                throw new ZygoteSecurityException("Peer is not permitted to specify "  
    29.                    + "an explicit invoke-with wrapper command");  
    30.            }  
    31.        }  
    32.    }  
    33.   
    34.    /** 
    35.     * Applies zygote security policy for SEAndroid information. 
    36.     * 
    37.     * @param args non-null; zygote spawner arguments 
    38.     * @param peer non-null; peer credentials 
    39.     * @throws ZygoteSecurityException 
    40.     */  
    41.    private static void applyseInfoSecurityPolicy(  
    42.            Arguments args, Credentials peer, String peerSecurityContext)  
    43.            throws ZygoteSecurityException {  
    44.        int peerUid = peer.getUid();  
    45.   
    46.        if (args.seInfo == null) {  
    47.            // nothing to check  
    48.            return;  
    49.        }  
    50.   
    51.        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {  
    52.            // All peers with UID other than root or SYSTEM_UID  
    53.            throw new ZygoteSecurityException(  
    54.                    "This UID may not specify SEAndroid info.");  
    55.        }  
    56.   
    57.        boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,  
    58.                                                     peerSecurityContext,  
    59.                                                     "zygote",  
    60.                                                     "specifyseinfo");  
    61.        if (!allowed) {  
    62.            throw new ZygoteSecurityException(  
    63.                    "Peer may not specify SEAndroid info");  
    64.        }  
    65.   
    66.        return;  
    67.    }  

理所当然的,在启动一个新的进程时,frameworks/base/core/java/android/os/Process.java中也会加入SEAndroid信息seInfo。

你可能感兴趣的:(android)