`

Android权限控制代码分析

 
阅读更多
前在文章介绍过android系统管理层次:http://www.2cto.com/kf/201204/127682.html ,这里就核心代码分析一下




android系统充分利用了linux的用户权限管理方法,所以如果需要移植到其它系统,这一块也是一个相当不小的工作量。那么android系统到底是如何使用这些的有利因素呢?




首先需要知道linux权限的两个基本知识:


1、 一个用户可以属于多个组.

2、 一个文件只能属于某个组。




这里主要是在AndroidManifest.xml中声明权限,主要是通过在AndroidManifest.xml中显示地声明应用程序需要的权限,防止应用程序错误的使用服务,不恰当访问资源。

Android中每种权限都用一个独立的标签表示.如:

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

当在安装(Install) 应用程序时,Android就会给予一个UID。这个UID可连结到该应用程序的 AndroidManifest.xml档案的内容。所User在安装你的应用程序时,在屏幕上的窗口里可以检视这个 AndroidManifest.xml档案的内容。在检视时,用户会看到你对应用程序的目的、权限等说明。当你接受这支程序的意图、权限说明之后,Android就安装它,并给它一个UID。万一在你的应用程序执行期间有越轨(企图做出非权限范围)的行为时,用户将会得到Android的警告讯息。

下面是两个安装程序安装时的界面

\\





这两个应用其实代码是一样的,唯一的不同就是它们的AndroidManifest.xml,图2的AndroidManifest.xml中多了如下内容:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

即需要使用存储设备和录音设备,在安装的时候,就会提示用户它需要的权限。Android里面是怎么去控制的呢?




在安装apk的时候,会解析这个AndroidManifest.xml,把相应的信息保存起来。

代码路径:frameworks\base\core\java\android\content\pm\PackageParser.java


最终调用的是private Package parsePackage(

        Resources res, XmlResourceParser parser, int flags, String[] outError)

这个函数,在里面对AndroidManifest.xml进行了解析,其中就有

    private Package parsePackage(
        Resources res, XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {


       ...

      String tagName = parser.getName();
      if (tagName.equals("application")) {
       ...
      } else if (tagName.equals("permission-group")) {
          if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("permission")) {
          if (parsePermission(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("permission-tree")) {
          if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("uses-permission")) {
          sa = res.obtainAttributes(attrs,
                  com.android.internal.R.styleable.AndroidManifestUsesPermission);

          // Note: don't allow this value to be a reference to a resource
          // that may change.
          String name = sa.getNonResourceString(
                  com.android.internal.R.styleable.AndroidManifestUsesPermission_name);

          sa.recycle();

          if (name != null && !pkg.requestedPermissions.contains(name)) {
              pkg.requestedPermissions.add(name.intern());
          }

          XmlUtils.skipCurrentTag(parser);
      }

     ...

   }





这里对它使用的权限进行了解析。

这里保存的都是"android.permission.WRITE_EXTERNAL_STORAGE"这样的字符串,在解析完后,会调用grantPermissionsLP函数获取对应的group_id,

代码路径:frameworks\base\services\java\com\android\server\PackageManagerService.java


private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
...
    if (allowed) {
        if (!gp.grantedPermissions.contains(perm)) {
            changedPermission = true;
            gp.grantedPermissions.add(perm);
            gp.gids = appendInts(gp.gids, bp.gids);
        } else if (!ps.haveGids) {
            gp.gids = appendInts(gp.gids, bp.gids);
        }
    } else {
        Slog.w(TAG, "Not granting permission " + perm
                + " to package " + pkg.packageName
                + " because it was previously installed without");
    }
              
...
}

这里把相应的组都保存到了gids中。

当应用程序启动的过程中会调用

private final void startProcessLocked(ProcessRecord app,

            String hostingType, String hostingNameStr)

代码路径:frameworks\base\services\java\com\android\server\am\ActivityManagerService.java

private final void startProcessLocked(ProcessRecord app,
        String hostingType, String hostingNameStr) {
...
    try {
        int uid = app.info.uid;
        int[] gids = null;
        try {
            gids = mContext.getPackageManager().getPackageGids(
                    app.info.packageName);
        } catch (PackageManager.NameNotFoundException e) {
            Slog.w(TAG, "Unable to retrieve gids", e);
        }
      
...
    int pid = Process.start("android.app.ActivityThread",
          mSimpleProcessManagement ? app.processName : null, uid, uid,
          gids, debugFlags, null);      
}


这里就是获取前面保存的gids,再后面调用创建了一个新的进程,这里传的参数就有gids



创建新进程利用jni最终调用 forkAndSpecializeCommon 函数 (路径:\dalvik\vm\native\dalvik_system_Zygote.c)


static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
    pid_t pid;


    uid_t uid = (uid_t) args[0];
    gid_t gid = (gid_t) args[1];
    ArrayObject* gids = (ArrayObject *)args[2];
    u4 debugFlags = args[3];
    ArrayObject *rlimits = (ArrayObject *)args[4];
    int64_t permittedCapabilities, effectiveCapabilities;
...
    pid = fork();
    if (pid == 0) {
        int err;
        /* The child process */
  
        err = setgroupsIntarray(gids);

        if (err < 0) {
            LOGE("cannot setgroups(): %s", strerror(errno));
            dvmAbort();
        }

        err = setrlimitsFromArray(rlimits);

        if (err < 0) {
            LOGE("cannot setrlimit(): %s", strerror(errno));
            dvmAbort();
        }


        err = setgid(gid);
        if (err < 0) {
            LOGE("cannot setgid(%d): %s", gid, strerror(errno));
            dvmAbort();
        }

        err = setuid(uid);
        if (err < 0) {
            LOGE("cannot setuid(%d): %s", uid, strerror(errno));
            dvmAbort();
        }      
        ...
}


我们看到在子进程里调用setgroupsIntarray设置该进程所属的组,这样它就拥有了该组的权限。也通过setgid及setuid决定了应用程序的uid及gid值。



举个例子:

我们新建一Android工程,读取其应用的uid/gid值,贴入如下代码:

try{

        java.lang.Process process = Runtime.getRuntime().exec("id");

        InputStream input = process.getInputStream();

        byte[] bytes = new byte[1204];

        int len;

        while((len = (input.read(bytes))) > 0)

        {

        System.out.print(new String(bytes, 0, len));

        }

        input.close();

这里运行运行linux的id命令的代码,id命令的功能是输出当前用户的uid,主要的group id和所在的group

在其AndroidManifest.xml添加如下权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

运行后看log里面有:

\




这里面就有groups=1015




再看下android_filesystem_config.h里面

代码路径:

system\core\include\private

有如下代码

#define AID_ROOT             0  /* traditional unix root user */

#define AID_SYSTEM        1000  /* system server */

#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */

...

#define AID_WIFI          1010  /* wifi subsystem */
#define AID_ADB           1011  /* android debug bridge (adbd) */
#define AID_INSTALL       1012  /* group for installing packages */
#define AID_MEDIA         1013  /* mediaserver process */
#define AID_DHCP          1014  /* dhcp client */
#define AID_SDCARD_RW     1015  /* external storage write access */





再看下platform.xml

路径:frameworks\base\data\etc

有如下配置

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >

        <group gid="sdcard_rw" />

</permission>

这样就把android.permission.WRITE_EXTERNAL_STORAGE 、"sdcard_rw"、以及 1015组关联起来了




我们再看下当sd卡挂载上去后目录的权限:

ls -l

drwxr-xr-x root     system            1970-01-01 08:00 obb

drwxr-xr-x root     system            1970-01-01 08:00 asec

drwx------ root     root              1970-01-01 08:00 secure

d---rwxr-x system   sdcard_rw          2012-03-29 17:11 sdcard

可以看到对于sd卡,组用户具有读写权限,而我们的应用也加入了这个组,这样它就可以操作sdcard了。




当一个应用需要操作sdcard而没有在AndroidManifest.xml添加相应的权限时,就不能成功完成。

以下是同一个程序,一个有在AndroidManifest.xml添加WRITE_EXTERNAL_STORAGE权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。

\




摘自 andyhuabing的专栏
分享到:
评论

相关推荐

    Android_安全架构及权限控制机制剖析.pdf

    简介: Android 是业界流行的开源移动平台,受到...本文从 Android 层次化安全架构入手,详细地介绍 Android 平台的安全架构及其权限控制机制,涵盖 Android 应用程序权限申请方法等,并从源代码实现层面来解析该机制。

    疯狂Android讲义源码

     1.5 Android应用结构分析 24  1.5.1 创建一个Android应用 24  1.5.2 自动生成的R.java 26  1.5.3 res目录说明 27  1.5.4 Android应用的清单文件:  AndroidManifest.xml 28  1.5.5 应用程序权限说明 29  ...

    Android基础 布局、数据存储访问、XML系列化解析和SharedPreferences入门

    4、Android权限控制 Android中的权限控制类似于Linux中对于文件系统的权限控制。 5、XML序列化解析 这是Android中另外一种存储文件的形式,文中详细的介绍了序列化和解析的操作步骤。 最最重要的是包含源码

    Android6.0权限问题解决

    apk22之后Android把一些涉及用户个人信息的权限都做了默认没有权限处理,需要用户确认才可以: 用户不需要在安装软件的时候一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请进行授权。举例...

    Android安全架构及权限控制机制剖析

    本文从Android层次化安全架构入手,详细地介绍Android平台的安全架构及其权限控制机制,涵盖Android应用程序权限申请方法等,并从源代码实现层面来解析该机制。Android作为一个移动设备的平台,其软件层次结构包括了...

    (android)服务器控制多个客户端播放音乐+源码

    一台手机做为服务器,其他手机做为客户端,所有客户端用socket连接上服务器,客户端不断读取socket中的数据,解析出命令后控制歌曲的播放。 http://blog.csdn.net/tovey19911011/article/details/40861481

    Android例子源码不弹框手机是否Root检测例子.zip

    经过分析,这是由于小米有自身的权限控制系统而导致。只需要在第二种方法的基础上,再另外判断文件拥有者对这个文件是否具有可执行权限(第4个字符的状态),就基本可以确定手机是否root了。这种方法基本可以判断所有...

    Android代码-Launcher3

    Android 5.0 版本及以上可能会出现因为相同权限声明而不能安装的问题 如果你想要可以在Eclipse编译的版本,可以看这个tag: GOODBYE_ECLIPSE 我写了几篇博客来讲解Launcher3桌面什么的,有兴趣的可以看看这里: ...

    新版Android开发教程.rar

    将会支持 Google 可能发布的手机操作系统或者应用软件,共同开发名为 Android 的开放源代码的移动 系 统。 谷歌早在 2002 年就进入了移动领域,可是由于目前的手机操作系统企业和手机企业相对封闭,提高了 行业的...

    Android开发与应用——张荣,原书配套课件

    3.4 Android应用程序权限 3.5 Activity及其生命周期 3.5.1 什么是Activity 3.5.2 Activity生命周期 3.6 Intent简介 3.6.1 Intent属性与过滤器 3.6.2 Intent启动系统Activity 3.7 小结 练习 第4章 ...

    Google Android SDK开发范例大全(完整版附部分源码).pdf

    包含部分书中源码 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1 安装AndroidSDK与ADTplug...

    精通ANDROID 3(中文版)1/2

    1.6 利用Android源代码  1.7 本书的示例项目  1.8 小结  第2章 设置开发环境  2.1 设置环境  2.1.1 下载JDK 6  2.1.2 下载Eclipse 3.6  2.1.3 下载Android SDK  2.1.4 命令行窗口  2.1.5 安装ADT...

    黑马程序员 安卓学院 万元哥项目经理 分享220个代码实例

    |--android 控制对话框位置 |--android 根据uri获取路径 |--android 模拟器错误 |--android 横竖屏切换 |--android 获取mac地址 |--android 获取sd卡状态 |--android 设置apn |--android 调节屏幕亮度 |--android ...

    Google Android sdk 开发范例大全 部分章节代码

    8.16 访问网站LoginAPI——远程服务器验证程序运行权限 8.17 地震速报!——HttpURLConnection与Service侦测服务 第9章 Google服务与Android混搭 9.1 Google帐号验证Token——AuthSub 9.2 Google搜索——...

    Android编程实现手机震动功能的方法

    主要介绍了Android编程实现手机震动功能的方法,结合实例形式分析了Android实现手机震动功能的核心代码与权限控制操作技巧,需要的朋友可以参考下

    基于静态代码分析和简单机器学习算法实现的安卓恶意应用鉴别系统

    控制流分析:分析代码中的控制流程,包括条件判断、循环和函数调用等。这有助于理解程序的逻辑流程,并发现控制流程中可能存在的问题,如死循环、条件判断错误等。 API调用分析:提取API调用的上下文信息,包括API的...

Global site tag (gtag.js) - Google Analytics