目前移動領(lǐng)域已經(jīng)出現(xiàn)了相當(dāng)部分的安全問題脸狸,新的惡意軟件層出不窮,另一方面泥彤,企業(yè)對敏感數(shù)據(jù)保密性意識日益提高吟吝,作為移動開發(fā)者,有責(zé)任對最終用戶的隱私和安全承擔(dān)更多責(zé)任浙宜。
本文主要討論移動安全存儲策略粟瞬,設(shè)計了安全加固方案称开,實現(xiàn)了移動應(yīng)用逆向分析鳖轰、安全代碼自動注入等全流程的一鍵式加固工具,并進行了驗證焰轻。
1.移動數(shù)據(jù)存儲
Android平臺實現(xiàn)數(shù)據(jù)存儲的5種基本方式:
數(shù)據(jù)共享(SharedPreferences)
--內(nèi)部存儲(File)
--SQLite數(shù)據(jù)庫存儲
--外部存儲
--網(wǎng)絡(luò)存儲
本文主要討論前三種數(shù)據(jù)存儲類型辱志,實現(xiàn)了加密解密SDK狞膘,并實現(xiàn)對APP的安全存儲注入。首先我們來簡單討論下Android中數(shù)據(jù)存儲的位置——考慮數(shù)據(jù)安全挽封,有必要更改android應(yīng)用的存儲位置嗎已球?
在非root設(shè)備上,數(shù)據(jù)已可通過沙盒得到很好地安全保護(當(dāng)然辅愿,我們也不可以完全忽略設(shè)備存在內(nèi)核漏洞或虛擬機漏洞時可能發(fā)生的后果)智亮。“自定義存儲位置”也是一種設(shè)計方式点待,使破解者難以確定存儲的路徑阔蛉,并完全控制了存儲實現(xiàn),以進行加密和解密保護癞埠。但這樣做聋呢,失去了Android自身完善的特權(quán)分離機制(安全沙盒),將數(shù)據(jù)文件完全暴露在沙盒外遭笋,弊大于利坝冕,實際上意義不大。
因此這里仍選擇沙盒作為數(shù)據(jù)的存儲位置瓦呼,接下來主要討論對于root的設(shè)備喂窟,如何增強數(shù)據(jù)的安全性。在root設(shè)備上央串,需要通過數(shù)據(jù)擦除或數(shù)據(jù)加密磨澡,實現(xiàn)數(shù)據(jù)的安全存儲,降低數(shù)據(jù)暴露的可能性质和。
2.加密方案選擇
涉及到了加密稳摄,我們需要選擇一種加密算法,加密算法的選擇無窮無盡饲宿,本文采用AES加密算法厦酬,選擇128位秘鑰加密方式。以下是三種存儲類型在AES算法下的具體實現(xiàn)方式:
SharedPreferences存儲加密解密方式:對key和value同時加密瘫想,存儲類型都為String類型仗阅,數(shù)據(jù)讀取時根據(jù)需要進行類型轉(zhuǎn)換。
File文件存儲加密解密方式:對數(shù)據(jù)流進行加密解密国夜。
SQLite數(shù)據(jù)庫存儲加密解密方式:基于Sqlcipher進行實現(xiàn)减噪。
3.代碼注入方案設(shè)計
3.1注入方案比較
在”哪里”或者如何將我們提供的SDK注入到已有APP中,以及如何修改此APP中的實現(xiàn)车吹。這里注入的層面可以是:java字節(jié)碼(.class)或DEX字節(jié)碼(.smali)筹裕,二者分別對應(yīng)反編譯和反匯編過程。這里選擇后者作為注入方案窄驹,原因如下:
(1) smali語法定義明確朝卒,易于直接替換對象類型;
(2)在反編譯流程中乐埠,更直接扎运。
3.2 DEX字節(jié)碼
Dalvik虛擬機運行Dalvik字節(jié)碼,由java字節(jié)碼轉(zhuǎn)換而來饮戳,又稱為” Dalvik匯編語言”,是Dalvik指令集組成的代碼洞拨,Dalvik指令集是Dalvik虛擬機為自己專門設(shè)計的一套指令集扯罐。但嚴(yá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é)碼的布局與選項不同键菱,一些字節(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ù)是兩個:一個是java.lang.String類型造虎,一個是int類型,兩個參數(shù)分別保存在寄存器v2和v3中纷闺,返回值是android.content.SharedPreferences類型算凿,并把返回值保存到v1寄存器中。
Dalvik字節(jié)碼中使用了寄存器保存變量和參數(shù)犁功。我們知道氓轰,Java虛擬機基于棧架構(gòu),程序需頻繁從棧上讀取或?qū)懭霐?shù)據(jù)波桩,會耗費CPU(指令分派戒努、內(nèi)存訪問次數(shù));Dalvik虛擬機基于寄存器架構(gòu)镐躲,數(shù)據(jù)訪問通過寄存器直接傳遞储玫。兩者都為每個線程維護一個PC計數(shù)器與調(diào)用棧,PC計數(shù)器以字節(jié)為單位記錄當(dāng)前運行位置距離方法開頭的偏移量萤皂。不同的是撒穷,Java棧記錄Java方法調(diào)用的活動記錄,以幀為單位保存線程的運行狀態(tài)裆熙,每調(diào)用一個方法就會分配新的棧幀壓入Java棧上端礼,方法返回則彈出并撤銷相應(yīng)的棧幀;而Dalvik棧維護一份寄存器表入录。
每個Dalvik寄存器都是32位蛤奥,對于大于這個長度的類型,用兩個相鄰寄存器存儲僚稿。寄存器被設(shè)計為兩種表示方法:v命名法和p命名法凡桥。具體請參考相關(guān)資料。
4.加固流程設(shè)計
App加固總流程如下:
apk反匯編 -> sdk注入 -> Smali文件掃描 -> 代碼修正 -> 正向匯編 -> apk簽名
5.加固流程實現(xiàn)
這里以SharedPreferences存儲為例蚀同,例舉SDK和代碼修正的實現(xiàn)方式缅刽。(其他步驟實現(xiàn)略)
5.1安全存儲SDK
SDK中提供了針對SharedPreferences存儲類型的加密解密方式啊掏,我們對外提供了一個SecureSharedPreferences類,它實現(xiàn)了android.content.SharedPreferences接口衰猛,并實現(xiàn)了接口所提供的各個方法迟蜜,將android.content.SharedPreferences類型實例作為參數(shù),傳遞給構(gòu)造方法啡省。當(dāng)讀取數(shù)據(jù)時娜睛,使用這個參數(shù)進行查詢并返回值,當(dāng)存儲數(shù)據(jù)時冕杠,在edit方法處微姊,返回一個實現(xiàn)android.content.SharedPreferences.Editor接口的類,進行數(shù)據(jù)的添加分预。
這樣的SDK設(shè)計使得代碼注入可行兢交,并只需要較少的修正量。
5.2 代碼修正
在調(diào)用SharedPreferences存儲時笼痹,某種實現(xiàn)方式可以是(java代碼):
SharedPreferences sp =context.getSharedPreferences("config", 0);
我們需要將其修正為調(diào)用我們提供的SDK方式來實例化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晴裹,而且之后不會再對它們進行使用,因而它們是可用的兩個寄存器救赐。這里我們選擇v3寄存器:
move-result-object v3
接下來新建一個SecureSharedPreferences對象涧团,并將它放到某寄存器下,這個寄存器需要是java中sp對象存儲的寄存器经磅,并且用這個寄存器的值進行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.驗證實踐
這里我們將整個流程所需的各個依賴包轧叽、庫文件苗沧、各個工具、代碼修正實現(xiàn)及我們提供的SDK集成到一起炭晒,形成一個一鍵式的加固工具(bat形式)待逞。以一個DEMO為例,DEMO中實現(xiàn)了以上三種存儲方式的數(shù)據(jù)存儲和讀取网严,其中存儲都以默認方式(明文)實現(xiàn)识樱,沒有進行安全加密。
將DEMO APK放到工作目錄下,啟動并得到加固后的安全APK(secure_demo.apk)牺荠,其中demo文件夾為中間過程產(chǎn)生的數(shù)據(jù)文件。
將它運行到一個root的Android測試機上驴一,并使用R.E.Explorer工具查看存儲休雌,可見數(shù)據(jù)已經(jīng)過了加密存儲,并能夠正確的解密并獲得原始數(shù)據(jù)肝断。SQLite數(shù)據(jù)庫對存儲本身進行了加密杈曲,因而加密后,通過工具(android內(nèi)置查看器或SQLite Expert Professional查看工具)已無法打開db文件胸懈。
7.總結(jié)與討論
本文主要討論了Android設(shè)備數(shù)據(jù)存儲安全問題担扑,重點關(guān)注于針對一個已有的、非安全存儲的移動應(yīng)用趣钱,如何將其加固為一個達到安全存儲結(jié)果的應(yīng)用涌献。
希望各位讀者們批評指正,并如若有更好的實踐或心得首有,及時交流和討論燕垃,十分感謝。最后附加了實現(xiàn)中幾點細節(jié)的討論:
(1)ART架構(gòu)
Android Art架構(gòu)和Dalvik架構(gòu)主要區(qū)別在于應(yīng)用的執(zhí)行是否使用即時編譯器井联,對于Art架構(gòu)來說卜壕,以軟件安裝即時性為犧牲,完成了代碼編譯烙常,以提高程序運行效率轴捎,而Dalvik架構(gòu)仍然需要即時編譯器的輔助。而無論是哪種架構(gòu)蚕脏,與本文中討論的加固方案應(yīng)該是不發(fā)生沖突的侦副。
(2)AES Seed
AES算法中是目前電子數(shù)據(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ù)保護目標(biāo)的話,需要對加密方式另行評估璧瞬。
感謝您讀完了本文户辫,如果您覺得文章內(nèi)容對您有用處,請贊賞支持下吧嗤锉。您的鼓勵是我極大地動力 :-)渔欢!