1、前言
今天咱們來看一下Android中一個衆人熟悉的一個屬性:shareUserId,關於這個屬性可能你們都很熟悉了,最近在開發項目,用到了這個屬性,雖然知道一點知識,可是感受仍是有些迷糊,因此就寫篇文章來深刻研究一下。java
關於Android中的sharedUserId的概念這裏就簡單介紹一下:android
Android給每一個APK進程分配一個單獨的空間,manifest中的userid就是對應一個分配的Linux用戶ID,而且爲它建立一個沙箱,以防止影響其餘應用程序(或者其餘應用程序影響它)。用戶ID 在應用程序安裝到設備中時被分配,而且在這個設備中保持它的永久性。數據庫
一般,不一樣的APK會具備不一樣的userId,所以運行時屬於不一樣的進程中,而不一樣進程中的資源是不共享的,在保障了程序運行的穩定。而後在有些時候,咱們本身開發了多個APK而且須要他們之間互相共享資源,那麼就須要經過設置shareUserId來實現這一目的。緩存
經過Shared User id,擁有同一個User id的多個APK能夠配置成運行在同一個進程中.因此默認就是能夠互相訪問任意數據. 也能夠配置成運行成不一樣的進程, 同時能夠訪問其餘APK的數據目錄下的數據庫和文件.就像訪問本程序的數據同樣。安全
用法也很簡單:app
在須要共享資源的項目的每一個AndroidMainfest.xml中添加shareuserId的標籤。
android:sharedUserId="com.example"
id名自由設置,但必須保證每一個項目都使用了相同的sharedUserId。一個mainfest只能有一個Shareuserid標籤。測試
1. SELinux 背景知識ui
詳細瞭解 Android 8.0 SELinux,能夠參閱 Google 官方文檔插件
1.1 DAC 與 MAC翻譯
在 SELinux 出現以前,Linux 上的安全模型叫 DAC,全稱是 Discretionary Access Control,翻譯爲自主訪問控制。
DAC 的核心思想很簡單,就是:進程理論上所擁有的權限與執行它的用戶的權限相同。好比,以 root 用戶啓動 Browser,那麼 Browser 就有 root 用戶的權限,在 Linux 系統上能幹任何事情。
顯然,DAD 管理太過寬鬆,只要想辦法在 Android 系統上獲取到 root 權限就能夠了。那麼 SELinux 是怎麼解決這個問題呢?在 DAC 以外,它設計了一種新的安全模型,叫 MAC(Mandatory Access Control),翻譯爲強制訪問控制。
MAC 的理論也很簡單,任何進程想在 SELinux 系統上幹任何事情,都必須在《安全策略文件》中賦予權限,凡是沒有出如今安全策略文件中的權限,就不行。
關於 DAC 和 MAC,能夠總結幾個知識點:
1. Linux 系統先作 DAC 檢查。若是沒有經過 DAC 權限檢查,則操做直接失敗。經過 DAC 檢查以後,再作 MAC 權限檢查
2. SELinux 有本身的一套規則來編寫安全策略文件,這套規則被稱之爲 SELinux Policy 語言。
1.2 SEPolicy 語言
Linux中有兩種東西,一種死的(Inactive),一種活的(Active)。死的東西就是文件(Linux哲學,萬物皆文件。注意,萬不可狹義解釋爲File),而活的東西就是進程。此處的 死 和 活 是一種比喻,映射到軟件層面的意思是:進程能發起動做,例如它能打開文件並操做它。而文件只能被進程操做。
下面咱們能夠直接使用一個例子來看看效果:首先咱們弄一個插件工程:ShareUserIdPlugin這個工程很簡單,咱們編譯安裝運行便可。在弄一個宿主工程:ShareUserIdHost這裏有一個核心方法,咱們首先經過插件工程的包名:cn.wjdiankong.shareuseridplugin;建立出一個Context對象。這裏看到第二參數有兩個模式:Context.CONTEXT_INCLUDE_CODE:這個標誌是在咱們須要執行插件中的某段代碼須要加上的值。CONTEXT_IGNORE_SECURITY:這個標誌是必須的,是忽視安全性,若是沒有這個值的話,那麼咱們訪問什麼都是失敗的。獲得了Context變量以後,咱們下面就能夠經過反射來執行代碼和獲取資源了,這裏須要注意的是,必定要先拿到Context對應的ClassLoader,而後才能加載對應的類,ClassLoader必定是Context的,是插件工程中的類加載器。下面咱們運行結果看看:運行成功了啦~~是否是很簡單呢。下面若是咱們把CONTEXT_INCLUDE_CODE去掉,在運行:發現報錯了,找不到指定的類。因此若是想運行代碼的話,這個值必定要加上。咱們再把CONTEXT_IGNORE_SECURITY去掉,運行結果:看到了,爆出了安全錯誤,因此要想構形成功Context出來,必需要加上這個值。3、步入正題好了,到這裏咱們就介紹瞭如何經過包名構造一個Context變量出來,而後執行對應的代碼和獲取資源。那麼這個咱們看到工程中貌似沒有用到shareUserId這個屬性呢?那這個和咱們今天要介紹的知識點有什麼關係嗎?其實沒什麼關係,上面的例子只能說是作一個簡單的引子,那有些同窗可能困惑了,爲什麼都沒有使用shareUserId屬性,這兩件事還能夠作呢?那豈不是很不安全?其實咱們在接觸過逆向知識的時候會發現,關於Android中的一個App中的代碼和資源說的直白點其實沒有安全性可言,好比,我想獲取一個一個app中的指定資源,可使用反編譯或者直接解壓apk就能夠獲得,想看到app中的一段代碼的含義或者執行結果,反編譯也能夠作到,因此說這個說的直白點關於代碼和資源在Android中其實沒什麼安全性可說。有辦法能夠去搞定的。固然咱們在後面能夠用這種構造Context的方式,去實現咱們想要的一些功能,好比咱們知道了一個app的資源名或者是方法名,想直接在個人工程中用,那麼可使用這種方式就能夠啦,不過這個仍是很不靠譜的,固然也是一種方式,好比A應用實現了一個很複雜的一個方法,我本身的應用和他沒任何關係,可是也須要這個方法,那麼能夠直接使用這種方式去調用便可。可是前提是A應用安裝了。固然正規公司的app都不會這麼傻逼的去作的,其實咱們在研究逆向app的時候可能會用到哦~~那麼說了這麼多,shareUserId的屬性的最大做用是什麼呢?前面都說了,Android中每一個app都對應一個uid,每一個uid都有本身的一個沙箱,這是基於安全考慮的,那麼說到沙箱,咱們會想到的是data/data/XXXX/目錄下面的全部數據,由於咱們知道這個目錄下面的全部數據是一個應用私有的,通常狀況下其餘應用是沒有權限訪問的,固然root以後是另外狀況,這裏就很少說了。這裏只看沒有root的狀況,下面咱們在來看一個場景:A應用和B應用都是一家公司的,如今想在A應用中可以拿到B引用存儲的一些值,那麼這時候該怎麼辦呢?這時候就須要用到了shareUserId屬性了,可是這裏咱們在介紹shareUserId屬性前,咱們先來看一個簡單的例子:仍是使用上面的兩個工程:ShareUserIdPlugin中的MainActivity.java代碼以下:這裏很簡單,咱們使用SharedPreferences來存儲一個密碼,注意模式是:Context.MODE_PRIVATE,關於這裏,有不少種模式,後面會詳細介紹。下面在來看一下宿主工程中的代碼,獲取密碼。運行宿主工程結果:咱們看到運行結果打印出來了幾個值,我先無論其餘的,看到最後pwd的值是默認值,那說明咱們宿主工程中獲取插件工程中的密碼失敗了。咱們在去看看插件工程中那個shareperference的xml文件的權限:這裏使用root了以後查看的:-rw-rw----關於這個值,不瞭解的同窗能夠網上去看一些資料:Linux文件權限你分開三段來看:首位表明是目錄仍是文件,通常不用管,後面的三段每段3位,r表明可讀,w表明可寫,x表明可執行,第一段是表明文件所屬的用戶對它的權限,第二段是所屬用戶組的用戶對它的權限,第三段是其餘用戶對它的權限。第一段:rw- ,所屬用戶(好比是root)對這個文件可讀可寫第二段:rw- ,所屬用戶組用戶,對這個文件可讀可寫第三段:--- ,其餘用戶對這個文件什麼都幹不了那麼從上面的分析能夠看出來,這個文件對於其餘用戶(不一樣uid的)訪問是失敗的。因此咱們獲取密碼失敗。那麼這個xml的權限在哪裏設置的呢?其實就是在插件工程中的那個建立SharedPreferences的時候:其實Context提供了幾種模式:一、Context.MODE_PRIVATE:爲默認操做模式,表明該文件是私有數據,只能被應用自己訪問,在該模式下,寫入的內容會覆 蓋原文件的內容,若是想把新寫入的內容追加到原文件中。可使用Context.MODE_APPEND二、Context.MODE_APPEND:模式會檢查文件是否存在,存在就往文件追加內容,不然就建立新文件。三、Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用來控制其餘應用是否有權限讀寫該文件。MODE_WORLD_READABLE:表示當前文件能夠被其餘應用讀取;MODE_WORLD_WRITEABLE:表示當前文件能夠被其餘應用寫入咱們能夠查看源碼ContextImpl.java:這裏獲取一個SharedPreferencesImpl對象,這個對象是實現了SharedPreferences接口的。這裏咱們看到採用了緩存機制,將xml的名字和sp對象一一對應起來,因此咱們能夠得知,一個app中,最好簡化xml的個數,儘可能將值都定義到一個xml中,減小內存佔用。咱們在看看SharedPreferencesImpl.java類源碼:有一個全局變量存儲了mode值,再看看mMode在哪裏用到了:在writeToFile這個方法中用到了,這個方法其實後面會分析的,就是SP將內存中的值保存到磁盤中。而後再看看ContextImpl的setFilePermissionsFromMode方法:好了,到這裏,咱們能夠看到,經過傳遞進來的mode值,來設置文件的權限。那麼代碼看完了,下面咱們在改一下插件工程中的那個建立sp的代碼:Context.MODE_WORLD_READABLE|Context.MODE_WORLD_WRITEABLE 爲讀寫模式再來測試一下:看到這裏取出來密碼了,成功了,關於空指針後面會詳細介紹的,這裏先無論了。咱們再來看一下sp的xml文件權限:看到了,其餘用戶是能夠進行讀寫操做的了,因此取出來的密碼是成功的了。到這裏咱們就弄清楚了Context提供的那幾個建立sp文件的幾種模式的區別,因此咱們這裏也能夠看到,這個模式很重要,對於安全性來講,不過這個默認模式就是private的,也是挺好的。