詳解Android 安全機(jī)制
public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message)
public void enforceUriPermission(Uri uri, String readPermission, String writePermission,int pid, int uid, int modeFlags, String message)
2.2.2 實(shí)現(xiàn)分析
ContextImpl.java 中提供的 API ,其實(shí)都是由 ActivityManagerService 中的如下幾個(gè)接口進(jìn)行的封裝。
public int checkPermission(String permission, int pid, int uid) throws RemoteException; // 主要用于一般的 permission 檢查
public int checkUriPermission(Uri uri, int pid, int uid, int mode) throws RemoteException; // 主要用于 Content Uri 的 permission 檢查
n checkPermission 的實(shí)現(xiàn)分析
1. 如果傳入的 permission 名稱為 null ,那么返回 PackageManager.PERMISSION_DENIED 。
2. 判斷調(diào)用者 uid 是否符合要求 。
1 ) 如果 uid 為 0 ,說(shuō)明是 root 權(quán)限的進(jìn)程,對(duì)權(quán)限不作控制。
2 ) 如果 uid 為 system server 進(jìn)程的 uid ,說(shuō)明是 system server ,對(duì)權(quán)限不作控制。
3 ) 如果是 ActivityManager 進(jìn)程本身,對(duì)權(quán)限不作控制。
4 )如果調(diào)用者 uid 與參數(shù)傳入的 req uid 不一致,那么返回 PackageManager.PERMISSION_DENIED 。
3. 如果通過(guò) 2 的檢查后,再 調(diào)用 PackageManagerService.checkUidPermission ,判斷 這個(gè) uid 是否擁有相應(yīng)的權(quán)限,分析如下 。
1 ) 首先它通過(guò)調(diào)用 getUserIdLP ,去 PackageManagerService.Setting.mUserIds 數(shù)組中,根據(jù) uid 查找 uid (也就是 package )的權(quán)限列表。一旦找到,就表示有相應(yīng)的權(quán)限。
2 ) 如果沒(méi)有找到,那么再去 PackageManagerService.mSystemPermissions 中找。這些信息是啟動(dòng)時(shí),從/system/etc/permissions/platform.xml 中讀取的。這里記錄了一些系統(tǒng)級(jí)的應(yīng)用的 uid 對(duì)應(yīng)的 permission 。
3 )返回結(jié)果 。
n 同樣 checkUriPermission 的實(shí)現(xiàn) 主要在 ActivityManagerService 中,分析如下:
1. 如果 uid 為 0 ,說(shuō)明是 root 用戶,那么不控制權(quán)限。
2. 否則,在 ActivityManagerService 維護(hù)的 mGrantedUriPermissions 這個(gè)表中查找這個(gè) uid 是否含有這個(gè)權(quán)限,如果有再檢查其請(qǐng)求的是讀還是寫(xiě)權(quán)限。
關(guān)于簽名機(jī)制,其實(shí)分兩個(gè)階段。
包掃描階段需要進(jìn)行完整性和證書(shū)的驗(yàn)證。普通 package 的簽名和證書(shū)是必須要先經(jīng)過(guò)驗(yàn)證的。具體做法是對(duì) manifest 下面的幾個(gè)文件進(jìn)行完整性檢查。完整性檢查包括這個(gè) jar 包中的所有文件。如果是系統(tǒng) package 的話,只需要使用 AndroidMenifest.xml 這個(gè)文件去提取簽名和驗(yàn)證信息就可以了。
在權(quán)限創(chuàng)建階段。如果該 package 來(lái)自 system img (系統(tǒng) app ),那么 trust it ,而且使用新的簽名信息去替換就的信息。前提是如果這個(gè) package 與其它 package 共享一個(gè) uid ,那么這個(gè)共享 uid 對(duì)應(yīng)的 sharedUser 中保存的簽名與之不一致,那么簽名驗(yàn)證失敗。有些時(shí)候系卸載一個(gè) app ,但是不刪除數(shù)據(jù),那么其 PackageSettings 信息會(huì)保留,其中會(huì)保存簽名信息。這樣再安裝是就會(huì)出現(xiàn)不一致。
3.1 Android Package 簽名原理
android 中系統(tǒng)和 app 都是需要簽名的。可以自己通過(guò) development/tools/make_key 來(lái)生成公鑰和私鑰。
android 源代碼中提供了工具 。/out/host/linux-x86/framework/signapk.jar 來(lái)進(jìn)行手動(dòng)簽名。簽名的主要作用在于限制對(duì)于程序的修改僅限于同一來(lái)源。系統(tǒng)中主要有兩個(gè)地方會(huì)檢查。如果是程序升級(jí)的安裝,則要檢查新舊程序的簽名證書(shū)是否一致,如果不一致則會(huì)安裝失??;對(duì)于申請(qǐng)權(quán)限的 protectedlevel 為 signature 或者 signatureorsystem 的,會(huì)檢查權(quán)限申請(qǐng)者和權(quán)限聲明者的證書(shū)是否是一致的。簽名相關(guān)文件可以從 apk 包中的 META-INF 目錄下找到。
signapk.jar 的源代碼在 build/tools/signapk ,簽名主要有以下幾步:
l 將除去 CERT.RSA , CERT.SF , MANIFEST.MF 的所有文件生成 SHA1 簽名
首先將除了 CERT.RSA , CERT.SF , MANIFEST.MF 之外的所有非目錄文件分別用 SHA-1 計(jì)算摘要信息,然后使用 base64 進(jìn)行編碼,存入MANIFEST.MF 中。 如果 MANIFEST.MF 不存在,則需要?jiǎng)?chuàng)建。存放格式是 entry name 以及對(duì)應(yīng)的摘要
l 根據(jù) 之前計(jì)算的 SHA1 摘要信息,以及 私鑰生成 一系列的 signature 并寫(xiě)入 CERT.SF
對(duì) 整個(gè) MANIFEST.MF 進(jìn)行 SHA1 計(jì)算,并將摘要信息存入 CERT.SF 中 。然后對(duì)之前計(jì)算的所有摘要信息使用 SHA1 再次計(jì)算數(shù)字簽名,并寫(xiě)入 CERT.SF 中。
l 把公鑰和簽名信息寫(xiě)入 CERT.RST
把之前整個(gè)的簽名輸出文件 使用私有密鑰計(jì)算簽名。同時(shí)將簽名結(jié)果,以及之前聲稱的公鑰信息寫(xiě)入 CERT.RSA 中保存。
3.2 Package 的簽名驗(yàn)證
安裝時(shí)對(duì)一個(gè) package 的簽名驗(yàn)證的主要邏輯在 JarVerifier.java 文件的 verifyCertificate 函數(shù)中實(shí)現(xiàn)。 其主要的思路是通過(guò)提取 cert.rsa 中的證書(shū)和簽名信息,獲取簽名算法等信息,然后按照之前對(duì) apk 簽名的方法進(jìn)行計(jì)算,比較得到的簽名和摘要信息與 apk 中保存的匹配。
第一步。提取證書(shū)信息,并對(duì) cert.sf 進(jìn)行完整性驗(yàn)證。
1. 先找到是否有 DSA 和 RSA 文件 ,如果找到則對(duì)其進(jìn)行 decode ,然后讀取其中的所有的證書(shū)列表(這些證書(shū)會(huì)被保存在 Package 信息中,供后續(xù)使用)。
2. 讀取這個(gè)文件中的簽名數(shù)據(jù)信息塊列表,只取第一個(gè)簽名數(shù)據(jù)塊。讀取其中的發(fā)布者和證書(shū)序列號(hào)。
3. 根據(jù)證書(shū)序列號(hào),去匹配之前得到的所有證書(shū),找到與之匹配的證書(shū)。
4. 從之前得到的簽名數(shù)據(jù)塊中讀取簽名算法和編碼方式等信息
5. 讀取 cert.sf 文件,并計(jì)算整個(gè)的簽名,與數(shù)據(jù)塊中的簽名(編碼格式的)進(jìn)行比較,如果相同則完整性校驗(yàn)成功。
第二步。使用 cert.sf 中的摘要信息,驗(yàn)證 MANIFEST.MF 的完整性。
在 cert.sf 中提取 SHA1-Digest-Manifest 或者 SHA1-Digest 開(kāi)頭的簽名 數(shù)據(jù)塊 ( -Digest-Manifest 這個(gè)是整個(gè) MANIFEST.MF 的摘要 信息,其它的是 jar 包中其它文件的摘要信息 ), 并逐個(gè)對(duì)這些數(shù)據(jù)塊 進(jìn)行驗(yàn)證。驗(yàn)證的方法是,現(xiàn)將 cert.sf 看做是很多的 entries ,每個(gè)entries 包含了一些基本信息,如這個(gè) entry 中使用的摘要算法( SHA1 等),對(duì) jar 包中的哪個(gè)文件計(jì)算了摘要,摘要結(jié)果是什么。 處理時(shí)先找到每個(gè)摘要數(shù)據(jù)開(kāi)中的文件信息,然后從 jar 包中讀取,然后使用 -Digest 之前的摘要算法進(jìn)行計(jì)算,如果計(jì)算結(jié)果與摘要數(shù)據(jù)塊中保存的信息的相匹配,那么就完成驗(yàn)證。
pid控制相關(guān)文章:pid控制原理
評(píng)論