1. 程式人生 > >Xposed AppSettings許可權管理原理分析

Xposed AppSettings許可權管理原理分析

轉:http://vbill.github.io/2015/02/13/xposed-appsettings/

本文分析一個許可權管理類Xposed模組的原始碼,主要分析許可權管理功能實現的原理。完全按照本人看程式碼的順序寫成。寫此文主要不是為了分析程式碼,而是總結這種分析程式碼的思路。所以,懶得看過程可以直接跳到程式碼分析

準備工作

下載好原始碼,還要在手機上把這個程式安裝好,這樣能直觀感受它的功能。

大致思路

我們最關鍵的任務是找到許可權控制的核心程式碼並弄明白它的功能。但是這麼多的檔案無從下手。我的想法是結合程式的實際操作,然後翻出來相應的程式碼。

直奔主題

先看AndroidManifest.xml,因為我們要找程式啟動介面。除了xposed的meta data,有個定義launcher activity的程式碼:

<activity
    android:name=".XposedModActivity"
    android:label="@string/app_name"
    android:configChanges="orientation|screenSize">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER"
/>
</intent-filter> </activity>

就是說類de.robv.android.xposed.mods.appsettings.XposedModActivity包含啟動介面的程式碼。

在手機上開啟程式,會看到啟動介面主要是一個ListView,每項裡面放著app的名稱和包名。點選其中某項會跳到新的Activity裡。那麼目標明確了,找這個跳轉程式碼。

我用startAcitivity為關鍵詞找到了:

list.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public
void onItemClick(AdapterView<?> parent, View view, int position, long id)
{ // Open settings activity when clicking on an application String pkgName = ((TextView) view.findViewById(R.id.app_package)).getText().toString(); Intent i = new Intent(getApplicationContext(), ApplicationSettings.class); i.putExtra("package", pkgName); startActivityForResult(i, position); } });

可見,ApplicationSettings這個類包含了新的Activity的程式碼。

新介面只有一個switch,開啟switch後所有的選項都出來了。我手機的左下角出現了一個叫”許可權管理“的按鈕。點開以後是一個對話方塊,裡面的ListView羅列了所有的應用許可權讓我們修改。單擊List裡的某項,許可權就被禁用了,同時許可權的字型由白變紫。

所以我先找這個按鈕的程式碼:

btnPermissions.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // set up permissions editor
                try {
                    final PermissionSettings permsDlg = new PermissionSettings(ApplicationSettings.this, pkgName, allowRevoking, disabledPermissions);
                        permsDlg.setOnOkListener(new PermissionSettings.OnDismissListener() {
                        @Override
                        public void onDismiss(PermissionSettings obj) {
                            allowRevoking = permsDlg.getRevokeActive();
                            disabledPermissions.clear();
                            disabledPermissions.addAll(permsDlg.getDisabledPermissions());
                        }
                    });
                    permsDlg.display();
                } catch (NameNotFoundException e) {
                }
            }
});

看來PermissionSettings就是許可權管理介面的類。在這個類中唯一被我發現的和ListView有關的程式碼:

// Load the list of permissions for the package and present them
    loadPermissionsList(pkgName);

    final PermissionsListAdapter appListAdapter = new PermissionsListAdapter(owner, permsList, disabledPerms, true);
    appListAdapter.setCanEdit(revokeActive);
    ((ListView) dialog.findViewById(R.id.lstPermissions)).setAdapter(appListAdapter);

所以說處理單擊ListView項並改變程式許可權的程式碼應該在別的地方。

只能是在Adapter的程式碼裡了:

if (allowEdits) {
    row.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!canEdit) {
                return;
            }

            TextView tv = (TextView) v.findViewById(R.id.perm_name);
            if ((tv.getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
                disabledPerms.remove(tv.getTag());
                tv.setPaintFlags(tv.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
                tv.setTextColor(Color.WHITE);
            } else {
                disabledPerms.add((String) tv.getTag());
                tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
                tv.setTextColor(Color.MAGENTA);
            }
        }
    });
}

在這裡disabledPerms是一個Set<String>型別的物件。顧名思義,這裡面方的可能是被禁用的許可權。上面程式碼並沒有直接處理許可權的部分。結合Diaglog介面有“確定”按鈕,推測最後程式先將許可權新增到Set中,然後統一禁止許可權。

那麼disabledPerms怎麼傳遞出去的呢?搜尋整個Adapter的程式碼,看到建構函式裡有一句:this.disabledPerms = disabledPerms;。再回頭看該才找到的PermissionSettings類裡和List唯一有關的程式碼裡有:

final PermissionsListAdapter appListAdapter = new PermissionsListAdapter(owner, permsList, disabledPerms, true);

所以disabledPerms就是我們要找的。下一步可以找PermissionSettings裡的這個disabledPerms裡的結果怎麼返回回去的。搜尋這個類裡的程式碼,找到了get方法:

/**
* Get the list of permissions in the disabled state
 */
public Set<String> getDisabledPermissions() {
    return new HashSet<String>(disabledPerms);
}

我們之前找到的ApplicationSettings裡面的btnPermissions.setOnClickListener裡有這麼一行:disabledPermissions.addAll(permsDlg.getDisabledPermissions());

另外disabledPermissions只有宣告沒有定義。在onCreate()方法裡它才被賦值:

// Setting for permissions revoking
allowRevoking = prefs.getBoolean(pkgName +   Common.PREF_REVOKEPERMS, false);
disabledPermissions = prefs.getStringSet(pkgName + Common.PREF_REVOKELIST, new HashSet<String>());

同時在private Map<String, Object> getSettings()這個方法結尾處有這麼兩行:

if (disabledPermissions.size() > 0)
    settings.put(pkgName + Common.PREF_REVOKELIST, new HashSet<String>(disabledPermissions));

方法的返回值就是settings。如果再看看整個方法的程式碼,可知這個應用的所有被修改內容全放到這個settings裡了。

onOptionsItemSelected裡出現了getSettings()的呼叫,同時還有很多sharedPreference的操作。在手機上,我們修改應用許可權,然後單擊右上角的儲存按鈕,彈出提示對話方塊,詢問是否結束程序以便下次啟動時採用新設定。根據這點找到程式碼:

prefsEditor.commit();

// Update saved settings to detect modifications later
initialSettings = newSettings;

// Check if in addition to saving the settings, the app should also be killed
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.settings_apply_title);
builder.setMessage(R.string.settings_apply_detail);
builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        // Send the broadcast requesting to kill the app
        Intent applyIntent = new Intent(Common.MY_PACKAGE_NAME + ".UPDATE_PERMISSIONS");
        applyIntent.putExtra("action", Common.ACTION_PERMISSIONS);
        applyIntent.putExtra("Package", pkgName);
        applyIntent.putExtra("Kill", true);
        sendBroadcast(applyIntent, Common.MY_PACKAGE_NAME + ".BROADCAST_PERMISSION");

        dialog.dismiss();
    }
});

整個工程只有一個PackagePermissions類是BroadcastReceiver類。開啟後發現這個類有大量的Xposed的hook函式。那麼問題來了:

  • 廣播接受器什麼時候開始工作的?
  • 哪些是hook許可權的操作?
  • hook是如何在開機時就開始了(否則沒辦法監控許可權)

在BroadcastReceiver裡叫initHooks()的靜態方法裡找到:

final Class<?> clsPMS = findClass("com.android.server.pm.PackageManagerService", XposedMod.class.getClassLoader());

// Listen for broadcasts from the Settings part of the mod, so it's applied immediately
findAndHookMethod(clsPMS, "systemReady", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        Context mContext = (Context) getObjectField(param.thisObject, "mContext");
        mContext.registerReceiver(new PackagePermissions(param.thisObject),
                new IntentFilter(Common.MY_PACKAGE_NAME + ".UPDATE_PERMISSIONS"),
                Common.MY_PACKAGE_NAME + ".BROADCAST_PERMISSION",
                null);
    }
});

// if the user has disabled certain permissions for an app, do as if the hadn't requested them
findAndHookMethod(clsPMS, "grantPermissionsLPw", "android.content.pm.PackageParser$Package", boolean.class,
        new XC_MethodHook() {
    @SuppressWarnings("unchecked")
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        String pkgName = (String) getObjectField(param.args[0], "packageName");
        if (!XposedMod.isActive(pkgName) || !XposedMod.prefs.getBoolean(pkgName + Common.PREF_REVOKEPERMS, false))
            return;

        Set<String> disabledPermissions = XposedMod.prefs.getStringSet(pkgName + Common.PREF_REVOKELIST, null);
        if (disabledPermissions == null || disabledPermissions.isEmpty())
            return;

        ArrayList<String> origRequestedPermissions = (ArrayList<String>) getObjectField(param.args[0], "requestedPermissions");
        param.setObjectExtra("orig_requested_permissions", origRequestedPermissions);

        ArrayList<String> newRequestedPermissions = new ArrayList<String>(origRequestedPermissions.size());
        for (String perm: origRequestedPermissions) {
            if (!disabledPermissions.contains(perm))
                newRequestedPermissions.add(perm);
            else
                // you requested those internet permissions? I didn't read that, sorry
                Log.w(Common.TAG, "Not granting permission " + perm
                        + " to package " + pkgName
                        + " because you think it should not have it");
        }

        setObjectField(param.args[0], "requestedPermissions", newRequestedPermissions);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        // restore requested permissions if they were modified
        ArrayList<String> origRequestedPermissions = (ArrayList<String>) param.getObjectExtra("orig_requested_permissions");
        if (origRequestedPermissions != null)
            setObjectField(param.args[0], "requestedPermissions", origRequestedPermissions);
    }

分析程式碼

BroadcastReceiver裡的註釋說:

/* Hook to the PackageManager service in order to
* - Listen for broadcasts to apply new settings and restart the app
* - Intercept the permission granting function to remove disabled permissions
*/

也就是說監聽器hook到了Android系統的包管理器類com.android.server.pm.PackageManagerService,並且hook了裡面的方法。所以上面提到的哪些是hook許可權的操作基本上解決了,程式碼之後詳細分析。那麼廣播什麼時候開始接收的?在IntelliJ裡搜尋BroadcastReceiver被用到的地方,發現:

  1. XposedMod類中initZygote方法裡出現PackagePermissions.initHooks();
  2. 剛才貼出的大段BroadcastReceiver裡出現mContext.registerReceiver裡面有它的建構函式。

initZygote是Xposed框架IXposedHookZygoteInit介面中要自己實現的方法。介面的原始碼為:

/**
* Hook the initialization of Zygote (the central part of the "Android OS")
*/
public interface IXposedHookZygoteInit extends IXposedMod {
/**
 * Called very early during startup of Zygote
 * @throws Throwable everything is caught, but will prevent further initialization of the module
 */
    public void initZygote(StartupParam startupParam) throws Throwable;

    public 
            
           

相關推薦

Xposed AppSettings許可權管理原理分析

轉:http://vbill.github.io/2015/02/13/xposed-appsettings/ 本文分析一個許可權管理類Xposed模組的原始碼,主要分析許可權管理功能實現的原理。完全按照本人看程式碼的順序寫成。寫此文主要不是為了分析程式碼,而是總結這

21天轉型容器實戰營(十二容器進階之Kubernetes 儲存管理原理分析

大綱 為何需要儲存卷? 普通儲存卷 應用中使用普通卷 持久化儲存卷(PV) 持久化儲存卷申明(PVC) 應用中使用持久化卷 為何需要儲存卷? 容器部署過程中一般有以下三種資料: - 啟動時需要的初始資料,可以是配置檔案 - 啟動過程中產生的臨時資料,該臨時資料需要多個容器間共享 - 啟動過程中產

21天轉型容器實戰營(十一容器進階之Kubernetes 儲存管理原理分析

[[email protected] day11]# cat pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-evs-auto-example namespace: default

Android許可權管理原理(含6.0-4.3)

Android 4.3-5.1 AppOpsManager動態許可權管理(官方不成熟的許可權管理) AppOpsManager 是Google在Android4.3-Android5.0引入的動態許可權管理方式,但是又與Google覺得不成熟,所以在每個發行版

django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解

知識清單 1.瞭解基於資源的許可權管理方式 2. 掌握許可權資料模型 3. 掌握基於url的許可權管理(不使用許可權框架的情況下實現許可權管理) 許可權管理原理知識 什麼是許可權管理 只要有使用者參與的系統一般都要有許可權管理,許可權管理實現對使用者訪問系統的控制。按照安全規則或安全策略

JAVAWEB開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解

/* SQLyog v10.2 MySQL - 5.1.72-community : Database - shiro ********************************************************************* */ /*!40101 SET NAMES

許可權管理需求分析

1、  一個使用者(User)屬於一個使用者組(user group),一個使用者組擁有多個使用者。 2、  一個使用者組(user group)只有一個管理員(Admin),並且只能對本使用者組進行操作。 3、  系統擁有一個超級管理員(super Admin),

Android許可權管理原理(含6.0)

前言Android系統在MarshMallow之前,許可權都是在安裝的時候授予的,雖然在4.3時,Google就試圖在原始碼裡面引入AppOpsManager來達到動態控制權限的目的,但由於不太成熟,在Release版本中都是把這個功能給隱藏掉的。在6.0之後,Google為

阿里系產品Xposed Hook檢測機制原理分析

導語: 在逆向分析android App過程中,我們時常用的用的Java層hook框架就是Xposed Hook框架了。一些應用程式廠商為了保護自家android App不被Xposed Hook框架給hook。於是想盡各種方法檢測自己產品是否被Xposed

然之OA許可權管理思路分析

一、許可權相關分析 1.然之OA許可權管理,通過組管理,給組新增新增使用者、應用、新增模組、新增操作。 2.概念解析 (1)使用者(User)可以擁有多個角色(Role),角色可以被分配給多

android superuser.apk 管理root許可權原理分析

1、雲中漫步部落格:  Android系統root破解原理分析     http://my.unix-center.net/~Simon_fu/?p=1069 2、雲中漫步 ? Android系統root破解原理分析(續) http://my.unix-center.net/~Simon_fu/?p=110

ELK6.2.3日誌分析工具elasticsearch x-pack6.2.3許可權管理Security模組和內建角色介紹

啟動elasticsearch 1.自定義設定密碼,三個內建賬號根據提示設定密碼 ./bin/x-pack/setup-passwords interactive 2.自動生成密碼 ./bin/x-pack/setup-passwords auto 3.在kibana通過DSL語句設定密碼

shell命令以及執行原理、檢視或修改掩碼(umask)、Linux許可權管理、Linux設定檔案訪問許可權(chmod)、粘滯位、修改檔案的擁有者(chown)、修改檔案的所屬組(chgrp)

shell命令以及執行原理:   Linux嚴格意義上說的是一個作業系統,我們稱之為”核心”,但是我們普通使用者,不能直接使用核心,而是通過核心的”外殼”程式,也就是所謂的shell,來與核心溝通。   Linux中的命令大多數都是可執行程式。但其實捕捉我們

程序管理Supervisor的原理分析和實戰練習

背景: 專案中遇到有些指令碼需要通過後臺程序執行,保證不被異常中斷,之前都是通過nohup、&、screen來實現,帶著能否做一個start/stop/restart/reload的服務啟動的想法找到裡Supervisor。關於supervisor的介紹在網上大致搜

許可權管理】基礎原理

這兩天在做移動端之餘也接觸一點兒許可權的知識。。基礎,考試,新生都有一定的瞭解,唯一對許可權是最陌生的。在AngularJs的摧殘下,不得不找點兒別的事兒來做做。原本想看一點兒關於shiro的東西,經人推薦了一套視訊,裡面講解shiro之前先講解了以下許可權的原理部分,感覺

版本管理-圖解Git的分支管理原理,深度分析fetch和pull區別

1分散式與集中式 1)Git是分散式,無SVN那樣的“中央伺服器”; 2)Git每個人電腦裡都有完整的版本庫,但是SVN的中央伺服器出了問題,所有人都沒法幹活了;Git除了每人都有一個版本庫外,還有一個遠端倉庫,SVN只有一個版本庫; 3)SVN適合於區域

MySQL的許可權管理系統的原理

MySQL使用GRANT和REVOKE命令授予或撤銷針對一個使用者的許可權。 授予的許可權可以分為多個層級: 全域性層級 資料庫層級 表層級 列層級 子程式層級 全域性級授權 GRANT ALL PRIVILEGES ON *.* to ‘nash_su’@’%’ id

【Shiro許可權管理】15.Shiro授權流程分析

注:該系列所有測試均在之前建立的Shiro3的Web工程的基礎上。 上一篇講解了Shiro授權的相關基礎知識,並實驗了“roles”攔截器的效果。可惜的是沒有給使用者 進行授權工作,所以沒有驗證擁有角色後的許可權校驗工作。下面就來講解一下Shiro的授權流程。 之前我們使用

Shiro系統許可權管理、及原理剖析

1.簡介                常用的JavaEE安全框架有shiro、spring security。shiro被應用非常廣泛,可以整合cas,搭建單點登入系統。spring security則被認為比較重,應用沒有shiro廣泛。shiro提供使用者名稱、

Spring3.1.0實現原理分析(二十二).Dao事務分析之事務管理器DataSourceTransactionManager

       大家好,開篇先來談談spring事務的優點吧,即spring事務的存在價值。首先它提供了非侵入式編碼的事務實現,這個是通過aop實現的,具體的實現過程之前也寫部落格分析了。        另外,spring還提供了一套標準的事務管理工作流程。簡單的說,事務管理