Android應用攻與防
安卓系統是由谷歌推出的一款移動終端,由于開源,所以國內出現了許多使用相關系統的廠商,比如小米,oppo,vivo,魅族等。
在國內,這些系統的用戶群體甚多。我們日常使用的社交、游戲、工作等應用,很多都裝在安卓系統上。
由于安卓系統的開源性,很多安全問題也隨之而來。那么,在安卓系統上運行的應用又是如何保證自身安全的呢?
本文通過實戰挖洞,展現一下Android應用的防守與攻擊方式。

1 簡介
為了避免應用被攻擊,各種應用在投產前會用一些技術手段進行加固。但是在持續對抗下,總會出現各種反加固的手段。
部分開發者可能忽視應用的安全性,各大應用市場上也有很多未采取加固手段或進行簡單加固的應用。
首先,我們來看一看一個未進行任何加固手段的應用,運行起來有多危險。
下圖是開發工程師加密身份證號的AES加密算法。AES算法能保證身份證號以密文形式在網絡上傳輸,不被他人竊取,將代碼打包安裝在我們手機中運行。

由于安卓應用包很容易獲取,源碼包被攻擊者拿去反編譯,反編譯結果如下圖:

對比開發者寫的代碼和攻擊者反編譯的代碼,幾乎完全一樣,攻擊者很容易分析出我們的代碼邏輯,從而造成破壞。
如上面程序,拿到我們加密算法邏輯,對傳輸中的加密內容進行解密,來獲取身份證號。
由此可見,沒有做任何安全防護的安卓源碼猶如“裸奔”。
2 防守原理
上面實例可見,沒有加固的安卓應用,運行起來是相當危險的。下面介紹幾種常見的應用加固方式。包括源碼混淆、應用加殼、應用運行環境檢測。
源代碼混淆保護
混淆是將代碼中的類、方法、變量等信息進行重命名,把它們改成一些如“a,b,c,d”這樣毫無意義的名字,這樣就增加了攻擊者逆向難度。混淆效果圖如下:

應用加殼保護
加殼是在二進制的程序中植入一段代碼,在運行的時候優先取得程序的控制權,做一些額外的工作。
應用加固的一種手法對原始二進制原文進行隱藏。簡單描述就是代碼隱藏了,只有程序運行時才會還原。
我們將上面加密身份證號的應用加殼,然后使用工具反編譯如下,已經看不到任何源碼信息。
攻擊者如果想要攻擊,第一步就需要脫殼。我這里是只進行簡單的加固,對安卓的關鍵dex文件整體加固,只有在運行的時候才會還原原有dex文件。當然還有其他不同類型的加殼手段。

安卓應用加殼大致經歷了四代:
第一代——殼Dex整體加密:
原理是對dex這個文件進行整體加密加殼,存放在APK的資源中,運行時將加密后的dex文件在內存中解密,并讓Dalvik虛擬機動態加載執行。
第二代——殼Dex抽取:
通過將代碼中的方法名和方法體進行分離,對分離的方法體進行加密處理,同時被加密的方法體中注入解密接口。運行加固包,執行被加密函數時,通過調用其中的解密接口,對當前方法進行解密。
第三代——殼混合加密:
結合第一、二代的加固優點,根據應用的實際情況采用不同的混合加固方式,從而達到加固效果和強度的最優化。
第四代——殼vmp保護:
VMP技術是將原來的可執行代碼轉換為自定義的字節碼,這些自定義的字節碼只能由自定義的虛擬機解釋器執行。
應用運行環境檢測保護
安卓應用如果不進行運行環境檢測,就像你在一個裝著針孔攝像頭的房間睡覺,卻不進行安全排查。顯然,這樣很容易引發很多安全問題。
安卓應用亦是如此。我們需要檢測手機是不是有root權限、裝了危險應用、設置了代理、開啟了調試模式等等。

比如檢測root。檢測在常用目錄下是否存在su命令,如存在,我們此時可以提示用戶有風險或者強行退出。
publicstatic boolean checkRootPathSU(){Filef=null;FinalStringkSuSearchPaths[]={"/system/bin/","/system/xbin/","/system/sbin/","/sbin/","/vendor/bin/"};try{for(inti=0;i{f=newFile(kSuSearchPaths[i]+"su");if(f!=null&&f.exists()){Log.i(LOG_TAG,"findsu in : "+kSuSearchPaths[i]);returntrue;}}}catch(Exceptione){e.printStackTrace();}returnfalse;}
檢測代理,發現我們在抓包,就斷開連接。
publicstatic boolean isWifiProxy(Context context) {Finalboolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >=Build.VERSION_CODES.ICE_CREAM_SANDWICH;StringproxyAddress;intproxyPort;if(IS_ICS_OR_LATER) {proxyAddress= System.getProperty("http.proxyHost");StringportStr = System.getProperty("http.proxyPort");proxyPort= Integer.parseInt((portStr != null ? portStr : "-1"));}else {proxyAddress= android.net.Proxy.getHost(context);proxyPort= android.net.Proxy.getPort(context);}Log.i("代理信息","proxyAddress:"+proxyAddress + "prot : " proxyPort")return(!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);}
3 實戰攻擊
國慶某SRC有三倍金幣活動,在家正好挖到某個App邏輯漏洞,發現該App不用充錢,也能成為永久VIP會員。
特意寫個過程分享一下。由于涉及到隱私問題,關鍵信息進行打碼處理。僅提供了詳細的攻擊過程,給大家做個參考。
挖洞思路如下:
反編譯—脫殼—抓包—繞過代理檢測—靜態分析—寫腳本hook—動態修改—攻擊成功。
拿到攻擊目標應用安裝包.apk文件。首先進行反編譯,查看源碼情況:

發現應用包已進行加殼處理。直接上工具進行脫殼。我這里使用frida_dexdump進行脫殼。
打印目標應用進程PID= 17460:

執行脫殼命令:

發現脫出來很多dex文件,為了方便分析,使用apktool進行解包,替換dex后重打包。
重打包apktoolb 原始版-oC:\Users\11.apk。

脫殼前后進行對比,發現脫出來很多源代碼:


手機運行該應用,發現有個會員功能,會員需要付費購買。
此時,心里想該應用會員校驗機制復雜不復雜?能不能繞過支付成為會員?
直接開搞。
手機設置代理,開啟BurpSuite進行抓包。一打開目標軟件就“網絡異常”,說明這個應用檢測了運行環境,檢測到手機開了代理就斷開連接。

查看脫殼后的源碼,發現進行了代理檢測:

既然有代理檢測我們就不設置代理,采用一種透明代理模式。
在BurpSuite中監聽80和443這兩個端口,并且將其設置為透明代理模式:

手機連接電腦,以下命令:

再查看Burpsuite,此時已經能抓到包,成功繞過代理檢測:

點擊關于我的信息,查看返回包,發現有關于會員信息的關鍵字VIP,VipstartTime,VipendTime,isVIP等等。記住這些關鍵字,然后去源代碼里面直接搜索,發現有相關信息:

靜態分析跟蹤調用邏輯關系,發現判斷是不是會員用了isVip進行判定,該字段值是true就是會員,false是非會員:

編寫Fridahook腳本,讓判斷會員的方法一直返回ture。把會員到期時間也進行更換,指定一個返回時間戳,后面還需要更換兩個關鍵參數,這里就不放出來了。

手機端開啟frida-server:

PC端執行我們的腳本:

進行了動態修改,此時查看手機頁面,已經成為了會員,繞過成功,使用一下會員才有的功能,發現亦能使用。

到這里攻擊已經完成,回顧一下破解思路:
首先拿到應用包,查看是否進行加殼——答案是已加殼。抓包發現進行了環境檢測,使用透明代理模式進行繞過。
隨后我們進行逆向分析代碼,成功找到繞過支付成為VIP的邏輯漏洞。
使用frida進行hook動態修改,修改運行中的代碼,成功成為會員,攻擊成功。
4 總結
對于安卓App應用來說,沒有絕對的安全。
在持續的攻防對抗下,防守技術和攻擊技術都不斷在進步與升級。就現有的防守技術來說,都只是提升了整體的安全性,提高了攻擊的成本。所以,對抗永無止境,我們要再接再厲。