App Ops
Android 4.3 隱藏功能 App Ops 分析
Android 4.3 剛剛發(fā)布,大家還在關(guān)心功能上有沒有什么亮點(diǎn)的時(shí)候,一個(gè)隱藏功能被AndroidPolice?報(bào)道出來。這個(gè)隱藏功能 Google 把它叫做 App Ops (Application Operations),也就是我們常說的權(quán)限管理。眾所周知,Android 的權(quán)限是飽受詬病的一大安全隱患。在 Android 4.3 面世以前,主要有三種方法對 App 進(jìn)行權(quán)限管理:重打包,patch 和注入。Android 4.3 的 App Ops 這一隱藏功能,一舉毀滅了之前所有的努力。不過,這也算是對之前工作的肯定吧,Google 終于加入權(quán)限管理了!本文將結(jié)合 Android 4.3 AOSP 代碼,對 App Ops 這一隱藏功能進(jìn)行詳細(xì)分析。
一、背景知識
這一部分介紹了權(quán)限管理的相關(guān)知識,如果了解的可以跳過。我將簡單介紹幾個(gè)概念,提供一個(gè)方向,想深入了解的可以自己搜索相關(guān)資料。
1. 什么叫權(quán)限管理?
Android 的安全保護(hù)源于權(quán)限,每個(gè) App 需要申請權(quán)限才能使用特定的功能,用戶在安裝的時(shí)候可以瀏覽 App 已申請的權(quán)限再決定是否安裝。但是隨著權(quán)限不斷增多和殘酷的現(xiàn)實(shí)證明了這一功能基本屬于雞肋。所以就出現(xiàn)了權(quán)限管理這一安全防護(hù),所謂權(quán)限管理,就是能夠手動配置某個(gè) App 的權(quán)限,進(jìn)而阻止惡意軟件以及防止隱私泄漏。當(dāng)然,更進(jìn)一步的權(quán)限管理,是能夠在 App 動態(tài)使用某個(gè)權(quán)限的時(shí)候,彈窗提示用戶允許和拒絕。這樣的權(quán)限管理就更加類似于 Windows 中的主動防御。
實(shí)現(xiàn)權(quán)限管理主要有三種方法:
當(dāng)然,Android 4.3 出現(xiàn) App Ops 以后,隨著新版本的不斷完善,這些方法可能就沒有存在的意義了。
2. 什么是 Binder 通信?
TBA
3. 什么是權(quán)限中的保護(hù)級別 (Protection Level) 以及系統(tǒng)權(quán)限 (System Permission)?
Android 文檔介紹的很清楚,protectionLevel 是 signatureOrSystem 的權(quán)限是系統(tǒng) App 和相同簽名的 App 才可以申請的。
4. 系統(tǒng)服務(wù)(System_Server)?
Android 啟動后有很多服務(wù)進(jìn)程提供比本功能,一些系統(tǒng)級的服務(wù)由 system_server 這個(gè)進(jìn)程進(jìn)行處理,其中包括電源管理,電話管理,包管理,藍(lán)牙管理等等,詳見Android的系統(tǒng)服務(wù)一覽。還有一類我們常用的服務(wù)是不包括在系統(tǒng)服務(wù)當(dāng)中的,比如說發(fā)短信,GPS等等,是由各自的服務(wù)進(jìn)程處理的。
5. 如何開啟 App Ops?
App Ops 其實(shí)就是 Settings 里面的一個(gè) Activity,所以找個(gè)方法直接把它打開即可。 有兩種簡單方法:
二、系統(tǒng)架構(gòu)
1. 架構(gòu)
App Ops 的基本架構(gòu)如圖所示:
其中包含兩個(gè)重要部分,一個(gè)叫做AppOpsService,另外一個(gè)叫AppOpsManager。
AppOpsService 是一個(gè)系統(tǒng)服務(wù),注冊的名字叫做 "appops",是最終檢查權(quán)限的服務(wù),其中權(quán)限管理存儲在 appops.xml 文件里面。
AppOpsManager 是一個(gè)訪問 AppOps 服務(wù)的類,同時(shí)有 Java 和 C 的實(shí)現(xiàn),為了應(yīng)對某些 native code 的服務(wù),比如說 Camera。
Settings 可以使用 AppOpsManger 來讀取和修改權(quán)限管理信息。當(dāng)其他 App 使用某個(gè)權(quán)限的時(shí)候,會通過 Binder 訪問服務(wù)端的某項(xiàng)服務(wù)。在服務(wù)端的各個(gè)服務(wù)中都插入了檢查權(quán)限的代碼,同樣通過使用 AppOpsManger 來檢查權(quán)限。
2. 代碼分析
2.1 AppOpsService?代碼在:/frameworks/base/services/java/com/android/server/AppOpsService.java
檢查用戶設(shè)定權(quán)限的函數(shù)是:checkOperation() 和 noteOperation(),區(qū)別是 checkOperation() 只是檢查 Operation 的情況,noteOperation() 還會記錄訪問時(shí)間等信息,代碼如下:
@Override public int checkOperation(int code, int uid, String packageName) {verifyIncomingUid(uid);verifyIncomingOp(code);synchronized (this) {Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);if (op == null) {return AppOpsManager.MODE_ALLOWED;}return op.mode;} }@Override public int noteOperation(int code, int uid, String packageName) {verifyIncomingUid(uid);verifyIncomingOp(code);synchronized (this) {Ops ops = getOpsLocked(uid, packageName, true);if (ops == null) {if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid+ " package " + packageName);return AppOpsManager.MODE_IGNORED;}Op op = getOpLocked(ops, code, true);if (op.duration == -1) {Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName+ " code " + code + " time=" + op.time + " duration=" + op.duration);}op.duration = 0;final int switchCode = AppOpsManager.opToSwitch(code);final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "+ switchCode + " (" + code + ") uid " + uid + " package " + packageName);op.rejectTime = System.currentTimeMillis();return switchOp.mode;}if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid+ " package " + packageName);op.time = System.currentTimeMillis();op.rejectTime = 0;return AppOpsManager.MODE_ALLOWED;} }修改某個(gè) App 的某項(xiàng)權(quán)限的函數(shù)是 setMode(),其中就是修改成員變量 mUidOps。mUidOps 是一個(gè)List 保存了某個(gè)package對應(yīng)的所有權(quán)限的mode (允許,忽略),具體代碼如下:
@Override public void setMode(int code, int uid, String packageName, int mode) {verifyIncomingUid(uid);verifyIncomingOp(code);ArrayList<Callback> repCbs = null;code = AppOpsManager.opToSwitch(code);synchronized (this) {Op op = getOpLocked(code, uid, packageName, true);if (op != null) {if (op.mode != mode) {op.mode = mode;ArrayList<Callback> cbs = mOpModeWatchers.get(code);if (cbs != null) {if (repCbs == null) {repCbs = new ArrayList<Callback>();}repCbs.addAll(cbs);}cbs = mPackageModeWatchers.get(packageName);if (cbs != null) {if (repCbs == null) {repCbs = new ArrayList<Callback>();}repCbs.addAll(cbs);}if (mode == AppOpsManager.MODE_ALLOWED) {// If going into the default mode, prune this op// if there is nothing else interesting in it.if (op.time == 0 && op.rejectTime == 0) {Ops ops = getOpsLocked(uid, packageName, false);if (ops != null) {ops.remove(op.op);if (ops.size() <= 0) {HashMap<String, Ops> pkgOps = mUidOps.get(uid);if (pkgOps != null) {pkgOps.remove(ops.packageName);if (pkgOps.size() <= 0) {mUidOps.remove(uid);}}}}}}scheduleWriteNowLocked();}}}if (repCbs != null) {for (int i=0; i<repCbs.size(); i++) {try {repCbs.get(i).mCallback.opChanged(code, packageName);} catch (RemoteException e) {}}} }修改 mode 之后會通過 writeState() 的函數(shù),最終寫入文件當(dāng)中,他用 appops.xml 來存儲 App Ops 信息,如果想了解 xml 中的各個(gè) tag 的語義,可以查看 writeState() 函數(shù)的實(shí)現(xiàn)。 簡單來說他會記錄 uid, 包名,mode,訪問時(shí)間,拒絕時(shí)間。
2.2 AppOpsManager?是一個(gè)管理類來和 AppOpsService 通信,他的函數(shù)實(shí)現(xiàn)比較簡單,重點(diǎn)是把控制轉(zhuǎn)移到 AppOpsService 就可以了。例如 noteOperation() 和 setMode() 在 AppOpsManager 里面調(diào)用他們的函數(shù)是 noteOp() 和 setMode(),代碼如下:
public int noteOp(int op, int uid, String packageName) {try {int mode = mService.noteOperation(op, uid, packageName);if (mode == MODE_ERRORED) {throw new SecurityException("Operation not allowed");}return mode;} catch (RemoteException e) {}return MODE_IGNORED; }public void setMode(int code, int uid, String packageName, int mode) {try {mService.setMode(code, uid, packageName, mode);} catch (RemoteException e) {} }2.3 攔截代碼?有了 noteOp() 函數(shù),但是要完成權(quán)限的動態(tài)檢查,還要在執(zhí)行某項(xiàng)權(quán)限的時(shí)候執(zhí)行 noteOp()。經(jīng)過分析,大概有十多個(gè)服務(wù)被插入了權(quán)限檢查函數(shù)。其中包括:ClipboardService, VibratorService, LocationManagerService, NotificationManagerService, GeofenceManager, GpsLocationProvider, IccSmsInterfaceManager, PhoneInterfaceManager, OutgoingCallBroadcaster, WifiService, ContentProvider, WindowManagerService 等等。更多詳細(xì)情況,可以查看 AndroidXRef 中的源碼。
三、實(shí)例分析
我們選取短信相關(guān)權(quán)限進(jìn)行分析,看看 App Ops 究竟是如何進(jìn)行權(quán)限控制的。首先我們了解一下發(fā)短信的流程,發(fā)短信的代碼很簡單,主要分兩個(gè)步驟:
SmsManager smsManager=SmsManager.getDefault(); smsManager.sendTextMessage("dest", "src", "content", null, null);第一步是通過 Binder 獲取 Server 段的發(fā)送短信服務(wù) isms service。第二部是調(diào)用遠(yuǎn)端的sendTextMessage() 函數(shù)。通過 Proxy Pattern,最后到達(dá)實(shí)現(xiàn)的方法,位于IccSmsInterfaceManager 中的 sendText()方法,我們先看一下代碼:
@Override public void sendText(String callingPackage, String destAddr, String scAddr,String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,"Sending SMS message");if (Rlog.isLoggable("SMS", Log.VERBOSE)) {log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +" text='"+ text + "' sentIntent=" +sentIntent + " deliveryIntent=" + deliveryIntent);}if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),callingPackage) != AppOpsManager.MODE_ALLOWED) {return;}mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); }遠(yuǎn)端 sendText() 函數(shù)首先通過 enforceCallingPermission() 函數(shù)來檢查 App 是否在 AndroidManifest.xml 中申請了 android.permission.SEND_SMS 的權(quán)限。然后在通過調(diào)用 mAppOps (AppOpsService 的服務(wù)端實(shí)例 AppOpsManager) 調(diào)用 noteOp() 函數(shù)檢查是否通過了用戶的權(quán)限設(shè)置。很有意思的是,如果沒有通過檢查,就直接 return。所以程序有時(shí)候會有提示,有時(shí)候假死然后退出。應(yīng)該說這是 Android 4.3 中 Google 還未完成的代碼,只是暫時(shí)完成基本功能而已。
四、結(jié)語
此次 Google 在 Android 4.3 中加入了權(quán)限管理的功能,雖然代碼還未完善,但是思路和框架設(shè)計(jì)的比較好,可能不會引出什么新的漏洞。Google 在迎合大眾的需要的同時(shí),故意放出了一個(gè)半成品,來測試一下大家的反映。估計(jì)照這個(gè)效果下去,Android 5 很有可能就能完善這項(xiàng)特性。到那時(shí)候我們就真的該考慮,我們還需要 root 么?或許混亂的國內(nèi)定制機(jī)還有他的廣闊市場吧。
總結(jié)
- 上一篇: (二十三)【模电】(波形的发生与信号的转
- 下一篇: linux ops 一些函数简介