Android應(yīng)用存儲安全與加固 |
來源:聚銘網(wǎng)絡(luò) 發(fā)布時間:2016-07-13 瀏覽次數(shù): |
信息來源:比特網(wǎng)
目前移動領(lǐng)域已經(jīng)出現(xiàn)了相當(dāng)部分的安全問題,新的惡意軟件層出不窮,另一方面,企業(yè)對敏感數(shù)據(jù)保密性意識日益提高,作為移動開發(fā)者,有責(zé)任對最終用戶的隱私和安全承擔(dān)更多責(zé)任。 本文主要討論移動安全存儲策略,設(shè)計(jì)了安全加固方案,實(shí)現(xiàn)了移動應(yīng)用逆向分析、安全代碼自動注入等全流程的一鍵式加固工具,并進(jìn)行了驗(yàn)證。 1.移動數(shù)據(jù)存儲 Android平臺實(shí)現(xiàn)數(shù)據(jù)存儲的5種基本方式: 數(shù)據(jù)共享(SharedPreferences) --內(nèi)部存儲(File) --SQLite數(shù)據(jù)庫存儲 --外部存儲 --網(wǎng)絡(luò)存儲 本文主要討論前三種數(shù)據(jù)存儲類型,實(shí)現(xiàn)了加密解密SDK,并實(shí)現(xiàn)對APP的安全存儲注入。首先我們來簡單討論下Android中數(shù)據(jù)存儲的位置——考慮數(shù)據(jù)安全,有必要更改android應(yīng)用的存儲位置嗎? 在非root設(shè)備上,數(shù)據(jù)已可通過沙盒得到很好地安全保護(hù)(當(dāng)然,我們也不可以完全忽略設(shè)備存在內(nèi)核漏洞或虛擬機(jī)漏洞時可能發(fā)生的后果)?!白远x存儲位置”也是一種設(shè)計(jì)方式,使破解者難以確定存儲的路徑,并完全控制了存儲實(shí)現(xiàn),以進(jìn)行加密和解密保護(hù)。但這樣做,失去了Android自身完善的特權(quán)分離機(jī)制(安全沙盒),將數(shù)據(jù)文件完全暴露在沙盒外,弊大于利,實(shí)際上意義不大。 因此這里仍選擇沙盒作為數(shù)據(jù)的存儲位置,接下來主要討論對于root的設(shè)備,如何增強(qiáng)數(shù)據(jù)的安全性。在root設(shè)備上,需要通過數(shù)據(jù)擦除或數(shù)據(jù)加密,實(shí)現(xiàn)數(shù)據(jù)的安全存儲,降低數(shù)據(jù)暴露的可能性。 2.加密方案選擇 涉及到了加密,我們需要選擇一種加密算法,加密算法的選擇無窮無盡,本文采用AES加密算法,選擇128位秘鑰加密方式。以下是三種存儲類型在AES算法下的具體實(shí)現(xiàn)方式: SharedPreferences存儲加密解密方式:對key和value同時加密,存儲類型都為String類型,數(shù)據(jù)讀取時根據(jù)需要進(jìn)行類型轉(zhuǎn)換。 File文件存儲加密解密方式:對數(shù)據(jù)流進(jìn)行加密解密。 SQLite數(shù)據(jù)庫存儲加密解密方式:基于Sqlcipher進(jìn)行實(shí)現(xiàn)。 3.代碼注入方案設(shè)計(jì)3.1注入方案比較 在”哪里”或者如何將我們提供的SDK注入到已有APP中,以及如何修改此APP中的實(shí)現(xiàn)。這里注入的層面可以是:java字節(jié)碼(.class)或DEX字節(jié)碼(.smali),二者分別對應(yīng)反編譯和反匯編過程。這里選擇后者作為注入方案,原因如下: (1) smali語法定義明確,易于直接替換對象類型; (2)在反編譯流程中,更直接。 3.2 DEX字節(jié)碼 Dalvik虛擬機(jī)運(yùn)行Dalvik字節(jié)碼,由java字節(jié)碼轉(zhuǎn)換而來,又稱為” Dalvik匯編語言”,是Dalvik指令集組成的代碼,Dalvik指令集是Dalvik虛擬機(jī)為自己專門設(shè)計(jì)的一套指令集。但嚴(yán)格說它不屬于正式語言,它的特點(diǎn)如下: Dalvik指令在調(diào)用格式上模仿了C語言的調(diào)用約定; --根據(jù)字節(jié)碼的大小與不同,一些字節(jié)碼添加了名稱后綴以消除歧義; 32位常規(guī)類型的字節(jié)碼未添加任何后綴 64位常規(guī)類型的字節(jié)碼添加-wide后綴 特殊類型的字節(jié)碼根據(jù)具體類型添加后綴,如-boolean、-byte、-char、-void等 --根據(jù)字節(jié)碼的布局與選項(xiàng)不同,一些字節(jié)碼添加了字節(jié)碼后綴以消除歧義,這些后綴通過在字節(jié)碼主名稱后添加斜杠(/)來分隔開; --在指令集的描述中,寬度值中每隔字母表示寬度為4位。 我們來看一下它對方法的定義: invoke-virtual {p0, v2, v3}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; move-result-object v1 這里調(diào)用了類android.content.Context的getSharedPreferences方法,其中p0寄存器存儲context實(shí)例,方法的參數(shù)是兩個:一個是java.lang.String類型,一個是int類型,兩個參數(shù)分別保存在寄存器v2和v3中,返回值是android.content.SharedPreferences類型,并把返回值保存到v1寄存器中。 Dalvik字節(jié)碼中使用了寄存器保存變量和參數(shù)。我們知道,Java虛擬機(jī)基于棧架構(gòu),程序需頻繁從棧上讀取或?qū)懭霐?shù)據(jù),會耗費(fèi)CPU(指令分派、內(nèi)存訪問次數(shù));Dalvik虛擬機(jī)基于寄存器架構(gòu),數(shù)據(jù)訪問通過寄存器直接傳遞。兩者都為每個線程維護(hù)一個PC計(jì)數(shù)器與調(diào)用棧,PC計(jì)數(shù)器以字節(jié)為單位記錄當(dāng)前運(yùn)行位置距離方法開頭的偏移量。不同的是,Java棧記錄Java方法調(diào)用的活動記錄,以幀為單位保存線程的運(yùn)行狀態(tài),每調(diào)用一個方法就會分配新的棧幀壓入Java棧上,方法返回則彈出并撤銷相應(yīng)的棧幀;而Dalvik棧維護(hù)一份寄存器表。 每個Dalvik寄存器都是32位,對于大于這個長度的類型,用兩個相鄰寄存器存儲。寄存器被設(shè)計(jì)為兩種表示方法:v命名法和p命名法。具體請參考相關(guān)資料。 4.加固流程設(shè)計(jì) App加固總流程如下: apk反匯編 -> sdk注入 -> Smali文件掃描 -> 代碼修正 -> 正向匯編 -> apk簽名 5.加固流程實(shí)現(xiàn) 這里以SharedPreferences存儲為例,例舉SDK和代碼修正的實(shí)現(xiàn)方式。(其他步驟實(shí)現(xiàn)略) 5.1安全存儲SDK SDK中提供了針對SharedPreferences存儲類型的加密解密方式,我們對外提供了一個SecureSharedPreferences類,它實(shí)現(xiàn)了android.content.SharedPreferences接口,并實(shí)現(xiàn)了接口所提供的各個方法,將android.content.SharedPreferences類型實(shí)例作為參數(shù),傳遞給構(gòu)造方法。當(dāng)讀取數(shù)據(jù)時,使用這個參數(shù)進(jìn)行查詢并返回值,當(dāng)存儲數(shù)據(jù)時,在edit方法處,返回一個實(shí)現(xiàn)android.content.SharedPreferences.Editor接口的類,進(jìn)行數(shù)據(jù)的添加。 這樣的SDK設(shè)計(jì)使得代碼注入可行,并只需要較少的修正量。 5.2 代碼修正 在調(diào)用SharedPreferences存儲時,某種實(shí)現(xiàn)方式可以是(java代碼): SharedPreferences sp =context.getSharedPreferences("config", 0); 我們需要將其修正為調(diào)用我們提供的SDK方式來實(shí)例化sp對象。DEX字節(jié)碼如下: invoke-virtual {p0, v2, v3}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; 我們首先將這個結(jié)果保存到一個寄存器下,這里可以使用v2或者v3寄存器,它們分別對應(yīng)兩個類型的參數(shù):java.lang.String和int,而且之后不會再對它們進(jìn)行使用,因而它們是可用的兩個寄存器。這里我們選擇v3寄存器: move-result-object v3 接下來新建一個SecureSharedPreferences對象,并將它放到某寄存器下,這個寄存器需要是java中sp對象存儲的寄存器,并且用這個寄存器的值進(jìn)行SharedPreferences方法的調(diào)用,如v1寄存器: new-instance v1,Lcom/…/SecureSharedPreferences; 繼而需要調(diào)用這個對象的構(gòu)造函數(shù),并把之前保存在v3寄存器中的值作為參數(shù)傳遞(SharedPreferences類型): invoke-direct {v1, v3}, Lcom/…/SecureSharedPreferences;->(Landroid/content/SharedPreferences;)V 這樣就完成了修正過程。 其他存儲類型以及調(diào)用方式的修正方式與以上類似,這里不再贅述。 6.驗(yàn)證實(shí)踐 這里我們將整個流程所需的各個依賴包、庫文件、各個工具、代碼修正實(shí)現(xiàn)及我們提供的SDK集成到一起,形成一個一鍵式的加固工具(bat形式)。以一個DEMO為例,DEMO中實(shí)現(xiàn)了以上三種存儲方式的數(shù)據(jù)存儲和讀取,其中存儲都以默認(rèn)方式(明文)實(shí)現(xiàn),沒有進(jìn)行安全加密。 將DEMO APK放到工作目錄下,啟動并得到加固后的安全APK(secure_demo.apk),其中demo文件夾為中間過程產(chǎn)生的數(shù)據(jù)文件。 將它運(yùn)行到一個root的Android測試機(jī)上,并使用R.E.Explorer工具查看存儲,可見數(shù)據(jù)已經(jīng)過了加密存儲,并能夠正確的解密并獲得原始數(shù)據(jù)。SQLite數(shù)據(jù)庫對存儲本身進(jìn)行了加密,因而加密后,通過工具(android內(nèi)置查看器或SQLite Expert Professional查看工具)已無法打開db文件。 7.總結(jié)與討論 本文主要討論了Android設(shè)備數(shù)據(jù)存儲安全問題,重點(diǎn)關(guān)注于針對一個已有的、非安全存儲的移動應(yīng)用,如何將其加固為一個達(dá)到安全存儲結(jié)果的應(yīng)用。 希望各位讀者們批評指正,并如若有更好的實(shí)踐或心得,及時交流和討論,十分感謝。最后附加了實(shí)現(xiàn)中幾點(diǎn)細(xì)節(jié)的討論: (1)ART架構(gòu) Android Art架構(gòu)和Dalvik架構(gòu)主要區(qū)別在于應(yīng)用的執(zhí)行是否使用即時編譯器,對于Art架構(gòu)來說,以軟件安裝即時性為犧牲,完成了代碼編譯,以提高程序運(yùn)行效率,而Dalvik架構(gòu)仍然需要即時編譯器的輔助。而無論是哪種架構(gòu),與本文中討論的加固方案應(yīng)該是不發(fā)生沖突的。 (2)AES Seed AES算法中是目前電子數(shù)據(jù)加密中被廣泛接受的方案,雖然密鑰是隨機(jī)生成的,但仍然需要一個密鑰種子,而這個秘鑰種子的安全性也是決定整個加密算法安全性的關(guān)鍵。在一個移動應(yīng)用中,密鑰種子的生成算法可以以本地代碼方式提供(.so),雖然so文件也不是絕對不可以被反編譯的,但是相對的會提高安全性。此外,谷歌還提出一種基于云存儲的方案: “Google recently announced a cloud-based storagesolution for apps, so you could consider storing the key there. Or its seemsthat getting the key outside the device, like on a server, is better.” (3)加密效率 AES算法處理過程復(fù)雜,導(dǎo)致大數(shù)據(jù)文件加密和解密效率不是很高,對于幾十兆的大文件加密,不是一個很好的選擇,因而企業(yè)在選擇大數(shù)據(jù)文件作為敏感數(shù)據(jù)保護(hù)目標(biāo)的話,需要對加密方式另行評估。
|