轉自http://bbs.pediy.com/showthread.php?t=183692
想必很多人都知道轟動一時android木馬OBAD,該木馬利用android設備管理器的漏洞,當用戶激活設備管理器后,該程序會在setting設備管理器列表隱藏,應用程序激活成設備管理器后,可以實現鎖屏、擦除用戶數據等功能,並且無法使用常規的卸載方式對其卸載,本文主要和介紹漏洞原理和漏洞補丁分享個人在分析過程中遇到的一些事情。
Android 在實現設備管理器時,需要再manifest.xml中注冊一個廣播接收者,代碼如下
<receiver android:name=".MyDeviceAdmin" android:permission="android.permission.BIND_DEVICE_ADMIN" > <meta-data android:name="android.app.device_admin" android:resource="@xml/device_admin" /> <intent-filter> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> </intent-filter> </receiver>
OBAD如何在setting的管理器列表隱藏呢,我們可以通過setting的源碼找到答案。相關代碼:packages\apps\Settings\src\com\android\settings\DeviceAdminSettings.java
主要方法:
void updateList() { mActiveAdmins.clear(); List<ComponentName> cur = mDPM.getActiveAdmins(); if (cur != null) { for (int i=0; i<cur.size(); i++) { mActiveAdmins.add(cur.get(i)); } } //獲得已經激活設備管理器列表mActiveAdmins mAvailableAdmins.clear(); // mAvailableAdmins setting的設備管理器列表 List<ResolveInfo> avail = getActivity().getPackageManager().queryBroadcastReceivers( new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED), PackageManager.GET_META_DATA); //獲取所有注冊了DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED即android.app.action.DEVICE_ADMIN_ENABLED action的廣播接收者列表avail int count = avail == null ? 0 : avail.size(); for (int i=0; i<count; i++) { ResolveInfo ri = avail.get(i); try { DeviceAdminInfo dpi = new DeviceAdminInfo(getActivity(), ri); if (dpi.isVisible() || mActiveAdmins.contains(dpi.getComponent())) { mAvailableAdmins.add(dpi); //如果應用注冊了包含該action的廣播接受者並且激活了設備管理器,就會在setting的設備管理器列表中顯示 } } catch (XmlPullParserException e) { Log.w(TAG, "Skipping " + ri.activityInfo, e); } catch (IOException e) { Log.w(TAG, "Skipping " + ri.activityInfo, e); } } getListView().setAdapter(new PolicyListAdapter()); }
但是沒有注冊android.app.action.DEVICE_ADMIN_ENABLED action的應用也可以激活為設備管理器。
這就導致了激活后的設備管理器無法在setting的設備管理器列表中顯示。
如何解決這種情況呢?下面我們一起來分析一下安全管家的設備管理器補丁。該補丁是一個正常的apk,反編譯之,代碼結構如下
主要類DeviceAdminProxy代碼如下:
//上面方法的主要內容就是對比直接通過DevicePolicyManager獲得的已經激活的設備管理器列表a和通過遍歷注冊了android.app.action.DEVICE_ADMIN_ENABLED action的列表b進行對比,如果發現列表a中的設備管理器沒有在列表b中出現,就調用如下代碼彈出取消激活的activity,讓用戶手動取消。
代碼中有一個RootMain的類,這個類在當能獲得root權限的時候使用,找到利用這個漏洞的設備管理器后直接調用DevicePolicyManager的removeActiveAdmin方法取消激活,該方法需要system以上權限才能執行。
關於android.app.action.DEVICE_ADMIN_ENABLED引起的漏洞都這里就結束了,下面說一下在分析中遇到的另外一件有趣的事。
前面講到setting獲取管理器列表時,有這么一段代碼,如下圖
代碼中實例DeviceAdminInfo對象,DeviceAdminInfo.java在frameworks\base\core\java\android\app\admin\DeviceAdminInfo.java
關鍵代碼:
之前我們在manifest的receiver中有如下配置:
如果不配置meta-data,DeviceAdminInfo類就會拋出異常,DeviceAdminSettings中獲取異常,按照上面圖中的代碼可以發現,同樣setting的設備管理器列表也無法顯示。這樣setting雖然不顯示了,但是,如果不配置meta-data,設備管理器時沒辦法正常激活的。怎么辦呢?用正常的設備管理器程序安裝,正常激活,然后刪除該程序的meta-data配置,生成apk,以更新安裝的形式安裝到android設備上。這個時候卸載該程序會發現,該程序已經激活為設備管理器,但是在setting設備管理器列表中找不到。因為設備管理器注冊了android.app.action.DEVICE_ADMIN_ENABLED,所以使用上述的設備管理漏洞補丁也是找不到的。
那么如何取消激活呢?從上面對於設備管理器漏洞補丁apk的分析可以得出兩種取消激活的方法分析(其實是三種):
一、 DevicePolicyManager獲得的已經激活的設備管理器列表,然后使用下面的代碼
啟動取消激活的activity。測試結果是行不通的,因為DeviceAdminAdd也需要解析meta-data中的信息。具體參見DeviceAdminAdd.java位於packages\apps\Settings\src\com\android\settings\ DeviceAdminAdd.java。
二、 獲取到激活的設備管理器列表后直接調用DevicePolicyManager的removeActiveAdmin方法取消激活,測試結果成功,但是需要system以上的權限。DevicePolicyManager.java位於frameworks\base\core\java\android\app\admin\DevicePolicyManager.java。
問題分析到這里的時候可能有些童鞋已經發現了,使用正常的apk激活設備管理器,然后使用異常的apk避免設備管理器被取消。那如果android設備重啟了呢?重啟以后還能保持激活嗎?答案是否定的,重啟設備以后,無法保持激活。原因是什么呢?分析如下:
主要代碼frameworks\base\services\java\com\android\server\DevicePolicyManagerService.java
Android設備重啟以后,SystemServer啟動DevicePolicyManagerService服務同時調用systemReady方法重新初始化設備管理器列表。如下:
loadSettingsLocked方法中調用findAdmin
代碼到這里就可以看到了,在獲取meta-data的時候會捕獲異常,返回空值。
所以第三種方法:重啟……
如有不對的地方歡迎指正。