Android Binder許可權檢查之clearCallingIdentity
轉自:https://www.zhihu.com/question/41003297
參考:
https://blog.csdn.net/thinkinwm/article/details/18311211
https://blog.csdn.net/windskier/article/details/6921672 //博主部落格也很優秀
作者:Gityuan
連結:https://www.zhihu.com/question/41003297/answer/89328987
來源:知乎
一、原始碼分析
(1)clearCallingIdentity方法,最終呼叫如下:
int64_t IPCThreadState::clearCallingIdentity() { int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; clearCaller(); return token; } void IPCThreadState::clearCaller() { mCallingPid = getpid(); //當前程序pid賦值給mCallingPid mCallingUid = getuid(); //當前程序uid賦值給mCallingUid }
- 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。
一句話總結:clearCallingIdentity作用是清空遠端呼叫端的uid和pid,用當前本地程序的uid和pid替代;
(2)restoreCallingIdentity方法,最終呼叫如下:
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
mCallingPid = (int)token;
}
從token中解析出PID和UID,並賦值給相應的變數。該方法正好是clearCallingIdentity的反過程。
一句話總結:restoreCallingIdentity作用是恢復遠端呼叫端的uid和pid資訊,正好是`clearCallingIdentity`的反過程;
到此,應該明白了從程式碼角度是如何實現的。
(3) 原始碼示例
上述過程主要在system_server程序的各個執行緒中比較常見(普通的app應用很少出現),比如system_server程序中的ActivityManagerService子執行緒,程式碼如下:
[–>ActivityManagerService.java]
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
//獲取遠端Binder呼叫端的pid
int callingPid = Binder.getCallingPid();
//清除遠端Binder呼叫端uid和pid資訊,並儲存到origId變數
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
//通過origId變數,還原遠端Binder呼叫端的uid和pid資訊
Binder.restoreCallingIdentity(origId);
}
}
文章startService中有講到attachApplication()的呼叫。該方法一般是system_server程序的子執行緒呼叫遠端程序時使用,而attachApplicationLocked方法則是在同一個執行緒中,故需要在呼叫該方法前清空遠端呼叫者的uid和pid,呼叫結束後恢復遠端呼叫者的uid和pid。
二、場景分析
場景1:首先執行緒A通過Binder遠端呼叫執行緒B,然後執行緒B通過Binder呼叫當前執行緒的另一個service或者activity之類的元件
分析:
- 執行緒A通過Binder遠端呼叫執行緒B:則執行緒B的IPCThreadState中的mCallingUid和mCallingPid儲存的就是執行緒A的UID和PID。這時線上程B中呼叫Binder.getCallingPid()和Binder.getCallingUid()方法便可獲取執行緒A的UID和PID,然後利用UID和PID進行許可權比對,判斷執行緒A是否有許可權呼叫執行緒B的某個方法。
- 執行緒B通過Binder呼叫當前執行緒的某個元件:此時執行緒B是執行緒B某個元件的呼叫端,則mCallingUid和mCallingPid應該儲存當前執行緒B的PID和UID,故需要呼叫clearCallingIdentity()方法完成這個功能。當執行緒B呼叫完某個元件,由於執行緒B仍然處於執行緒A的被呼叫端,因此mCallingUid和mCallingPid需要恢復成執行緒A的UID和PID,這是呼叫restoreCallingIdentity()即可完成。
一句話:圖中過程2(呼叫元件2開始之前)執行clearCallingIdentity(),過程3(呼叫元件2結束之後)執行restoreCallingIdentity()。
二、類比分析
看完場景分析,估計還有不少朋友感到迷惑,為何需要這兩個方法來多此一舉,直接檢測最初呼叫端的許可權不就行了嗎?為了更加形象明瞭地說明其用途,下面用一個生活中的場景來類比說明。
場景:假如你的朋友請你幫忙,給她(他)到你的公司以內部價購買公司的某個產品。
分析:這個過程分為兩個階段
- 第一階段:你的朋友請你幫忙的過程,這個過程並不一定所有朋友都會幫的,這時就需要一個許可權檢測,那麼在你的朋友”遠端呼叫”你執行任務時,你會記錄他的”Identity”資訊(比如是性別),有了資訊那麼就可以許可權檢測,不妨令許可權規則是如果這個朋友是女性則答應幫忙,否則就認定許可權不夠拒絕執行(可能黑客會想到先去一趟泰國,許可權控制可能相應需要打補丁了),若答應幫忙則進入第二階段,否則直接返回。
- 第二階段:你向自己所在公司的相關部門內購產品的過程,這個過程也並不是所有人都能許可權能夠內購的,只有自己公司的員工才行,否則你的朋友也不會找你幫忙了。 這個過程同樣需要許可權檢測,但是”Identity”儲存的是性別女的資訊,公司內購產品如果也以性別來判斷,那豈不是公司的所有男員工沒有許可權內購,那這公司就有點太坑了,這明顯不符合實情。 clearCallingIdentity()是時候該登場了,在第二階段開始之前,先執行clearCallingIdentity()過程,也就是把”Identity”資訊清空,替換為你的資訊(比如員工編碼ITCode之類的),那公司相關部門通過ITCode就可以直接判斷是否允許內購某產品。當第二階段完成後,也就是你已經購買到了公司產品,這時你需要將產品交付給你的朋友,需要restoreCallingIdentity,恢復”Identity”為女的資訊,這樣就該順便交付給你的女朋友。如果不恢復資訊,還是原來的ITCode,你交付的朋友可能是男的,另有其人,這樣就不科學了。
相信到此,大都能明白這兩個方法的作用,缺一不可,而且要成對出現。