PS:个人理解:当进程1通过Binder调用组件2时,会将进程1的pid及uid赋给组件2,并检测进程1的pid及uid是否有权限调用组件2.而后组件2需要调用组件3,此时组件2保存的pid及uid为进程1的,但是其实际运行在进程2中。此时调用clearCallingIdentity将组件2的保存的pid及uid转换为进程2的pid和uid并return原来保存的进程1的pid及uid的token。此时组件2调用组件3并检查权限。调用完成后,restoreCallingIdentity将组件2的pid及uid恢复为进程1的。

正文:

看过Android系统源代码的朋友,一定看到过Binder.clearCallingIdentity()Binder.restoreCallingIdentity()这两个方法,其定义在Binder.java文件:

  1. //作用是清空远程调用端的uid和pid,用当前本地进程的uid和pid替代;
  2. public static final native long clearCallingIdentity();
  3. //作用是恢复远程调用端的uid和pid信息,正好是`clearCallingIdentity`的反过程;
  4. public static final native void restoreCallingIdentity(long token);

这两个方法涉及的uid和pid,每个线程都有自己独一无二的IPCThreadState对象,记录当前线程的pid和uid,可通过方法Binder.getCallingPid()Binder.getCallingUid()获取相应的pid和uid。

clearCallingIdentity(), restoreCallingIdentity()这两个方法使用过程都是成对使用的,这两个方法配合使用,用于权限控制检测功能。

二、原理

从定义这两个方法是native方法,通过Binder的JNI调用,在android_util_Binder.cpp文件中定义了native方法所对应的jni方法。

2.1 clearCallingIdentity

[-> android_util_Binder.cpp]

  1. static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
  2. {
  3. //调用IPCThreadState类的方法执行
  4. return IPCThreadState::self()->clearCallingIdentity();
  5. }

2.1.1 IPC.clearCallingIdentity

[-> IPCThreadState.cpp]

  1. int64_t IPCThreadState::clearCallingIdentity()
  2. {
  3. int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
  4. clearCaller();
  5. return token;
  6. }
  7. void IPCThreadState::clearCaller()
  8. {
  9. mCallingPid = getpid(); //当前进程pid赋值给mCallingPid
  10. mCallingUid = getuid(); //当前进程uid赋值给mCallingUid
  11. }
  • mCallingUid(记为UID),保存Binder IPC通信的调用方进程的Uid;
  • mCallingPid(记为PID),保存Binder IPC通信的调用方进程的Pid;

UID和PID是IPCThreadState的成员变量, 都是32位的int型数据,通过移位操作,将UID和PID的信息保存到token,其中高32位保存UID,低32位保存PID。然后调用clearCaller()方法将当前本地进程pid和uid分别赋值给PID和UID,最后返回token

2.2 restoreCallingIdentity

[-> android_util_Binder.cpp]

  1. static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
  2. {
  3. //token记录着uid信息,将其右移32位得到的是uid
  4. int uid = (int)(token>>32);
  5. if (uid > 0 && uid < 999) {
  6. //目前Android中不存在小于999的uid,当uid<999则抛出异常。
  7. char buf[128];
  8. jniThrowException(env, "java/lang/IllegalStateException", buf);
  9. return;
  10. }
  11. //调用IPCThreadState类的方法执行
  12. IPCThreadState::self()->restoreCallingIdentity(token);
  13. }

2.2.1 IPC.restoreCallingIdentity

[-> IPCThreadState.cpp]

  1. void IPCThreadState::restoreCallingIdentity(int64_t token)
  2. {
  3. mCallingUid = (int)(token>>32);
  4. mCallingPid = (int)token;
  5. }

token中解析出PID和UID,并赋值给相应的变量。该方法正好是clearCallingIdentity的反过程。

2.3 getCallingPid

[-> android_util_Binder.cpp]

  1. static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
  2. {
  3. return IPCThreadState::self()->getCallingPid();
  4. }

2.3.1 IPC.getCallingPid

[-> IPCThreadState.cpp]

  1. pid_t IPCThreadState::getCallingPid() const
  2. {
  3. return mCallingPid;
  4. }
  5. uid_t IPCThreadState::getCallingUid() const
  6. {
  7. return mCallingUid;
  8. }

2.4 远程调用

2.4.1 binder_thread_read

  1. binder_thread_read(){
  2. while (1) {
  3. struct binder_work *w;
  4. switch (w->type) {
  5. case BINDER_WORK_TRANSACTION:
  6. t = container_of(w, struct binder_transaction, work);
  7. break;
  8. case :...
  9. }
  10. if (!t)
  11. continue; //只有BR_TRANSACTION,BR_REPLY才会往下执行
  12. tr.code = t->code;
  13. tr.flags = t->flags;
  14. tr.sender_euid = t->sender_euid; //mCallingUid
  15. if (t->from) {
  16. struct task_struct *sender = t->from->proc->tsk;
  17. //当非oneway的情况下,将调用者进程的pid保存到sender_pid
  18. tr.sender_pid = task_tgid_nr_ns(sender,current->nsproxy->pid_ns);
  19. } else {
  20. //当oneway的的情况下,则该值为0
  21. tr.sender_pid = 0;
  22. }
  23. ...
  24. }

2.4.2 IPC.executeCommand

  1. status_t IPCThreadState::executeCommand(int32_t cmd)
  2. {
  3. BBinder* obj;
  4. RefBase::weakref_type* refs;
  5. status_t result = NO_ERROR;
  6. switch ((uint32_t)cmd) {
  7. case BR_TRANSACTION:
  8. {
  9. const pid_t origPid = mCallingPid;
  10. const uid_t origUid = mCallingUid;
  11. mCallingPid = tr.sender_pid; //设置调用者pid
  12. mCallingUid = tr.sender_euid;//设置调用者uid
  13. ...
  14. reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
  15. &reply, tr.flags);
  16. mCallingPid = origPid; //恢复原来的pid
  17. mCallingUid = origUid; //恢复原来的uid
  18. }
  19. case :...
  20. }
  21. }

关于mCallingPid、mCallingUid的修改过程:是在每次Binder Call的远程进程在执行binder_thread_read()过程, 会设置pid和uid. 然后在IPCThreadState的transact收到BR_TRANSACION则会修改mCallingPid、mCallingUid。

这里需要注意的是,当oneway的的情况下mCallingPid=0,不过mCallingUid可以拿到正确值。

三、用途

3.1 场景分析

场景:首先线程A通过Binder远程调用线程B,然后线程B通过Binder调用当前线程的另一个service或者activity之类的组件。

分析:

  1. 线程A通过Binder远程调用线程B:则线程B的IPCThreadState中的mCallingUidmCallingPid保存的就是线程A的UID和PID。这时在线程B中调用Binder.getCallingPid()Binder.getCallingUid()方法便可获取线程A的UID和PID,然后利用UID和PID进行权限比对,判断线程A是否有权限调用线程B的某个方法。
  2. 线程B通过Binder调用当前线程的某个组件:此时线程B是线程B某个组件的调用端,则mCallingUidmCallingPid应该保存当前线程B的PID和UID,故需要调用clearCallingIdentity()方法完成这个功能。当线程B调用完某个组件,由于线程B仍然处于线程A的被调用端,因此mCallingUidmCallingPid需要恢复成线程A的UID和PID,这是调用restoreCallingIdentity()即可完成。

一句话:图中过程2(调用组件2开始之前)执行clearCallingIdentity(),过程3(调用组件2结束之后)执行restoreCallingIdentity()

3.2 类比分析

看完场景分析,估计还有不少朋友感到迷惑,为何需要这两个方法来多此一举,直接检测最初调用端的权限不就行了吗?为了更加形象明了地说明其用途,下面用一个生活中的场景来类比说明。

场景:假如你的朋友请你帮忙,给她(他)到你的公司以内部价购买公司的某个产品。

分析:这个过程分为两个阶段

  • 第一阶段:你的朋友请你帮忙的过程,这个过程并不一定所有朋友都会帮的,这时就需要一个权限检测,那么在你的朋友”远程调用”你执行任务时,你会记录他的”Identity”信息(比如是性别),有了信息那么就可以权限检测,不妨令权限规则是如果这个朋友是女性则答应帮忙,否则就认定权限不够拒绝执行(可能黑客会想到先去一趟泰国,权限控制可能相应需要打补丁了),若答应帮忙则进入第二阶段,否则直接返回。
  • 第二阶段:你向自己所在公司的相关部门内购产品的过程,这个过程也并不是所有人都能权限能够内购的,只有自己公司的员工才行,否则你的朋友也不会找你帮忙了。 这个过程同样需要权限检测,但是”Identity”保存的是性别女的信息,公司内购产品如果也以性别来判断,那岂不是公司的所有男员工没有权限内购,那这公司就有点太坑了,这明显不符合实情。 clearCallingIdentity()是时候该登场了,在第二阶段开始之前,先执行clearCallingIdentity()过程,也就是把”Identity”信息清空,替换为你的信息(比如员工编码ITCode之类的),那公司相关部门通过ITCode就可以直接判断是否允许内购某产品。当第二阶段完成后,也就是你已经购买到了公司产品,这时你需要将产品交付给你的朋友,需要restoreCallingIdentity,恢复”Identity”为女的信息,这样就嗯呢该顺便交付给你的女朋友。如果不恢复信息,还是原来的ITCode,你交付的朋友可能是男的,另有其人,这样就不科学了。

相信到此,大家应该都能明白这两个方法的作用,缺一不可,而且要成对出现。

3.3 实例分析

上述过程主要在system_server进程的各个线程中比较常见(普通的app应用很少出现),比如system_server进程中的ActivityManagerService子线程,代码如下:

[–>ActivityManagerService.java]

  1. @Override
  2. public final void attachApplication(IApplicationThread thread) {
  3. synchronized (this) {
  4. //获取远程Binder调用端的pid
  5. int callingPid = Binder.getCallingPid();
  6. //清除远程Binder调用端uid和pid信息,并保存到origId变量
  7. final long origId = Binder.clearCallingIdentity();
  8. attachApplicationLocked(thread, callingPid);
  9. //通过origId变量,还原远程Binder调用端的uid和pid信息
  10. Binder.restoreCallingIdentity(origId);
  11. }
  12. }

文章startService流程分析中有讲到attachApplication()的调用。该方法一般是system_server进程的子线程调用远程进程时使用,而attachApplicationLocked方法则是在同一个线程中,故需要在调用该方法前清空远程调用者的uid和pid,调用结束后恢复远程调用者的uid和pid。

转自:http://gityuan.com/2016/03/05/binder-clearCallingIdentity/

Binder IPC的权限控制的更多相关文章

  1. Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?

    目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...

  2. SEAndroid安全机制对Binder IPC的保护分析

    在SEAndroid安全机制中,除了文件和属性,还有Binder IPC须要保护.Binder IPC是Android系统的灵魂,使用得相当广泛又频繁.比如,应用程序都是Binder IPC请求訪问系 ...

  3. 尝试asp.net mvc 基于controller action 方式权限控制方案可行性

    微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方 ...

  4. MongoDB 安全和访问权限控制

    MongoDB的访问控制能够有效保证数据库的安全,访问控制是指绑定Application监听的IP地址,设置监听端口,使用账户和密码登录 一,访问控制的参数 1,绑定IP地址 mongod 参数:-- ...

  5. WebGIS中快速整合管理多源矢量服务以及服务权限控制的一种设计思路

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在真实项目中,往往GIS服务数据源被其他多个信息中心或者第三方 ...

  6. ASP.NET MVC实现权限控制

    这篇分享一下 ASP.NET MVC权限控制.也就是说某一用户登录之后,某一个用户是否有权限访问Controller,Action(操作),视图等 想实现这些功能,需要在数据库创建好几个表:[User ...

  7. springmvc+spring+mybatis+maven项目集成shiro进行用户权限控制【转】

    项目结构:   1.maven项目的pom中引入shiro所需的jar包依赖关系 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  8. Appfuse:权限控制

    Appfuse的权限控制依赖于Struts的Menu机制,common下的menu.jsp是对菜单顺序的定义,详细的菜单项和菜单链接及权限再menu-config.xml中控制,如下: <Men ...

  9. .NET WebAPI 用ActionFilterAttribute实现token令牌验证与对Action的权限控制

    项目背景是一个社区类的APP(求轻吐...),博主主要负责后台业务及接口.以前没玩过webAPI,但是领导要求必须用这个(具体原因鬼知道),只好硬着头皮上了. 最近刚做完权限这一块,分享出来给大家.欢 ...

随机推荐

  1. spring mvc poi excel

    Util类 package com.common.util; public class ExportUtil { private XSSFWorkbook wb = null; private XSS ...

  2. spoj-TSUM Triple Sums

    题目描述 题解: 很吊的容斥+$FFT$,但是并不难. 首先,由于有重复,我们要容斥. 怎么办? 记录三个多项式, 只取一个:$w1$; 相同物体拿两个:$w2$; 相同物体拿三个:$w3$; 然后答 ...

  3. MySQL 8.0.12 基于Windows 安装教程

    第一步:到MySQL官网下载安装包:https://dev.mysql.com/downloads/mysql/ 第二步:将下载好的安装包(mysql-8.0.12-winx64 .zip)解压到相应 ...

  4. 导出csv文件(php实现)

    <?php namespace App\Library\lib; class CsvLib { /** * [构造函数] * */ public function __construct() { ...

  5. Spring Boot Web开发中Thymeleaf模板引擎的使用

    这里使用的是idea 1.新建Spring Boot项目 File-->New-->Project...,然后选择左边的Spring Initializr-->Next,可根据自己的 ...

  6. pwntools使用简介3

    连接 本地process().远程remote().对于remote函数可以接url并且指定端口. IO模块 下面给出了PwnTools中的主要IO函数.这个比较容易跟zio搞混,记住zio是read ...

  7. 【HDU 6000】Wash(贪心)

    Problem Description Mr.Panda is about to engage in his favourite activity doing laundry! He's brough ...

  8. 算法导论 第六章 2 优先队列(python)

    优先队列:     物理结构: 顺序表(典型的是数组){python用到list}     逻辑结构:似完全二叉树 使用的特点是:动态的排序..排序的元素会增加,减少#和快速排序对比 快速一次排完 增 ...

  9. Android Ubuntu 12.04 源码环境搭建

    $ sudo apt-get install git gnupg flex bison gperf build-essential \ zip curl libc6-dev libncurses5-d ...

  10. xtu DP Training B. Collecting Bugs

    B. Collecting Bugs Time Limit: 10000ms Memory Limit: 64000KB 64-bit integer IO format: %lld      Jav ...