Android安全防護之旅---幾行程式碼讓Android應用變得更加安全

編碼美麗發表於2018-07-16

我們在編碼美麗微信公眾號已經弄過了很多app了,不管是協議還是外掛,我們都是那麼一路走過來了,在操作的過程中也發現了很多問題就是應用不在乎安全問題帶來的後果,因為安全始終都是不可忽視的問題,辛辛苦苦寫的程式碼被人看的體無完膚對不起自己也對不起公司,所以如果你做了這幾件事至少可以防止一些人把你的app給無情的強姦了。本文就來總結一下不用加固方式也可以讓你的應用變得更加安全可靠。


一、程式碼資源混淆

這裡說的混淆不是說的傳統大家都知道的簡單混淆策略,而是高階一點的混淆策略,首先是程式碼混淆,大家可以參考小黃車app的程式碼:

640?wx_fmt=png

看到了吧人家把程式碼混淆成中國人可以看懂的資訊,可惜這樣的資訊對於我們破解來說就很麻煩了,關於怎麼做到的,之前的文章已經介紹了,大家可以查閱這裡:Android中把程式碼混淆成中文,當然可以簡單一點就是用 -classobfuscationdictionary實現也可以。程式碼混淆成這樣不算什麼,還有更厲害的就是把資源混淆成一坨屎的應用:

640?wx_fmt=png

看到了嗎資原始檔反編譯之後全是這鳥樣了,感覺世界都快崩潰了,這個方案暫時沒研究不過大家可以進入編碼美麗小密圈諮詢bin神找MT管理器作者諮詢方案。當把程式碼混淆成一坨屎,把資源混淆成一坨翔的時候,讓破解者頂著一坨翔破解吧。


二、防止應用被抓包

在混淆之後我們還是覺得不夠安全,因為有的人很賤,總是抓包偷窺我們的資料,不過我一般不會這麼賤因為我一般正大光明的抓包看資料,應用的網路訪問介面和返回資料有的時候很重要,所以為了防止應用不被幹,能用https的一定要用,雖然用了也不管用,因為我們有Xposed。用了https之後也要重寫SSLSocketFactory類,不懂這個技術可以看看這篇文章:Android中破解防止被抓包應用方案 有了這個還是不行,因為我們會反編譯幹掉這個類。還需要加上系統的api來判斷當前裝置有沒有掛代理不管是Fiddler還是VPN只要有就認為是不安全的,這兩個api方法很簡單:

System.getProperty("http.proxyHost"); 

System.getProperty("http.proxyPort"); 

正常情況下這兩個返回值是null,所以你們懂得怎麼做了,如果發現不為空那麼就表示當前裝置被掛代理了,那麼就有可能被抓包的風險了,這裡額外說一下破解者在除了用Fiddler抓包,還可以直接在手機上用Packet CaptureDebug Proxy這兩個工具進行抓包:

640?wx_fmt=png


不過可惜這兩個工具都用了系統的VPNService功能做的,所以這時候上面介面返回的值就不為空了,那麼這兩個工具就等於廢了。那麼加了這些就一定管用嗎?肯定不管用,因為要是管用那我下面還說什麼呢?因為這些都是Java層程式碼,所以安全點就把這些判斷放到native層做。只要找到了這個地方直接nop掉就好了。所以我們還得想辦法防止,抓包的時候我們要是隱藏我們的介面域名就好了,因為破解者通過抓包找到域名,然後再去Jadx中搜尋就定位到了引數加密地方了,那要是我們把域名做成動態下發的,不用域名訪問而是ip地址訪問,這些ip地址通過配置獲取,這樣被抓到了也搜不到增加破解難度。不過這些還是沒啥用的,因為你動態下發只要找到這個地方就會找到ip地址了,到時候還是一樣被幹了。所以我們還可以這麼做,網路請求用底層的socket進行訪問操作,這樣就不會被抓包了,可惜的是這世界上還有一個叫做tcpdump+WireShark的工具,也是於事無補。所以我已經瘋了,因為想不到什麼方案了。最後請求資料中一定要記得帶上簽名加密資訊,不要一味的放在請求引數中,我偷偷的告訴你很多破解者容易忽視請求頭資訊。

640?wx_fmt=png


三、防止應用被hook操作

有時候最煩人的就是自己的應用被別人hook了,感覺自己的腚被人捅了一樣,所以為了保護好自己的腚,我們需要做一些事防止被hook,這個就要學習人家支付寶了,同樣是腰間盤為什麼別人總是這麼突出:

640?wx_fmt=png

雖然支付寶的防護被一個帥氣英俊的美男子程式猿破了,但是還是要說他的方案也是備選之一,如何防止應用被hook住:


第一、Xposed框架將Hook資訊儲存在欄位fieldCache,methodCache,constructorCache 中, 利用java 反射機制獲取這些資訊,檢測Hook資訊中是否含有支付寶App中敏感的方法,欄位,構造方法
640?wx_fmt=png
第二、檢測程式中使用so名中包含關鍵"hack|inject|hook|call" 的資訊,這個主要是防止有的人不用這個Xposed框架,而是直接自己寫一個注入功能
640?wx_fmt=png
第三、root檢測原理是:是否含有su程式和ro.secure是否為1
640?wx_fmt=png

第四、防止被Hook的方式就是可以檢視XposedBridge這個類,有一個全域性的hook開關,所有有的應用在啟動的時候就用反射把這個值設定成true,這樣Xposed的hook功能就是失效了:

640?wx_fmt=png

第五、如果應用被Xposed進行hook操作之後,丟擲的異常堆疊資訊中就會包含Xposed字樣,所以可以通過應用自身內部丟擲異常來檢測是否包含Xposed欄位來進行防護:

640?wx_fmt=png

可惜雖然他很突出但是忘了一點就是把操作放到native層會好點,畢竟我不怎麼熟悉arm指令的,但是我會Frida。


四、防止應用被除錯

在你都加了上面的幾個操作之後,腚的確沒有被人捅了,但是感覺心中被人植入了蒼老師,讓你感覺渾身發熱。所以做了以上這麼多之後還有一點很重要就是不要讓你的應用被人家扒光了一樣看你的身體。因為現在很多破解者在靜態方式不成之後就採用了動態除錯辦法進行操作。所以我們為了讓應用更安全就做好防止反除錯操作

第一種:先佔坑,自己附加

程式碼非常簡單,在native層中加上這行程式碼即可:ptrace(PTRACE_TRACEME, 0, 0, 0);其中PTRACE_TRACEME代表:本程式被其父程式所跟蹤。其父程式應該希望跟蹤子程式,一般一個程式只能被附加一次,我們在破解除錯的時候都會附加需要除錯應用的程式,如果我們先佔坑,父程式附加自己,那麼後面在附加除錯就會失敗。加上這段程式碼我們執行之後看一下效果:

640?wx_fmt=png

我們在進行破解動態除錯的時候都知道附加程式的status檔案中的TracerPid欄位就是被除錯的程式pid,這裡執行程式之後,檢視程式對應的status檔案,發現TracerPid值就是程式的父程式pid值。那麼後面如果有程式在想附加除錯就是會失敗的。這種方式啟動一定的除錯作用,但是不是絕對安全的。關於解決這種反除錯方案後面再說。


第二種:除錯狀態檢查

這種方式是純屬藉助Android中的api進行檢驗,有兩種方法:
第一:檢查應用是否屬於debug模式
直接呼叫Android中的flag屬性:ApplicationInfo.FLAG_DEBUGGABLE,判斷是否屬於debug模式:

640?wx_fmt=png

這個其實就是為了防止現在破解者為了除錯應用將應用反編譯在AndroidManifest.xml中新增:android:debuggable屬性值,將其設定true。然後就可以進行除錯。

640?wx_fmt=png

新增這個屬性之後,我們可以用 dumpsys package [packagename] 命令檢視debug狀態:

640?wx_fmt=png

所以我們可以檢查應用的AppliationInfo的flag欄位是否為debuggable即可。不過這種方式也不是萬能的,後面會介紹如何解決這種反除錯問題。


第二:檢查應用是否處於除錯狀態

這個也是藉助系統的一個api來進行判斷:

android.os.Debug.isDebuggerConnected()

這個就是判斷當前應用有沒有被除錯,我們加上這段程式碼之後,之前IDA動態除錯的時候其中有一個步驟進行jdb連線操作:

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

當連線成功之後,這個方法就會返回true,那麼我們可以利用這個api來進行判斷當前應用是否處於除錯狀態來進行反除錯操作。但是這種方式不是萬能的,後面會介紹如何解決這種反除錯問題。


第三種:迴圈檢查埠

我們在之前破解逆向的時候,需要藉助一個強大利器,那就是IDA,在使用IDA的時候,我們知道需要在裝置中啟動android_server作為通訊,那麼這個啟動就會預設佔用埠23946:

640?wx_fmt=png

我們可以檢視裝置的tcp埠使用情況 cat /proc/net/tcp:

640?wx_fmt=png

其中5D8A轉化成十進位制就是23946,而看到uid是0,因為我們執行android_server是root身份的,uid肯定是0了。所以我們可以利用埠檢查方式來進行反除錯策略,當然這種方式不是萬能的,後面會詳細介紹如何解決這樣的反除錯方法。同時還要檢查有沒有android_server這個程式,有的話也是表示有可能被除錯了。

640?wx_fmt=png

當然還有最近很常用的Frida框架,他的埠號是27042和27043,以及程式名是frida-server。另可錯殺一千不能放過一個。


第四種:迴圈檢查TracerPid值

在第一種方式中,我們簡單的介紹瞭如果應用被除錯了,那麼他的TracerPid值就是除錯程式的pid值,而在使用IDA進行除錯的時候,需要在裝置端啟動android_server進行通訊,那麼被除錯的程式就會被附加,這就是android_server程式的pid值了:

640?wx_fmt=png

檢視一下android_server的pid值:

640?wx_fmt=png

所以我們可以在自己的應用中的native層加上一個迴圈檢查自己status中的TracerPid欄位值,如果非0或者是非自己程式pid(如果採用了第一種方案的話,這裡也是需要做一次過濾的);那麼就認為被附加除錯了。當然這裡還有一種方案,就是可以檢查程式列表中有沒有android_server程式,不過這種方式都不是萬能的,後面會詳細介紹如何解決這種反除錯方案。


五、簽名校驗防護策略

在做了上面的一些工作之後發現腚也不疼了,心中也坦然了,可是誰知道有些人太壞了偷偷的把應用的衣服給換了,本來是一個待入閨中的大家閨秀,結果被換了一套比基尼搖身一變成了外圍女擱誰誰受得了,所以為了不讓自己的應用衣服被換我們得買點膠水把衣服粘住,不要被人惡意拆解我們的應用然後二次打包謀取暴利,簽名校驗一般方案有兩種:

第一:直接在本地做防護,如果發現簽名不一致直接退出應用
第二:將簽名資訊攜帶請求引數中參與加密,服務端進行簽名校驗,失敗就返回錯誤資料即可

而這兩種方式也都不是最安全的防護,因為只要有簽名校驗的邏輯,在本地都可以進行過濾掉。特別用了我的kstools工具之後完全就處於遊離狀態了。不過簽名校驗不要按照傳統方式去做了因為那個已經眾人皆知不安全了,我們需要校驗各個檔案的資訊,比如微信的dex檔案校驗,阿里聚安全的簽名檔案校驗等高強度操作。對於檔案校驗也是最實際一點的,不要在用系統方法獲取簽名資訊校驗了,那個已經過時了。


六、加固方案

說了這麼多了感覺還是不安全,的確肯定不安全,因為上面的工作你要是還是放在java層去做那麼等於沒說,永遠記住NDK不僅僅是為了效能效率考慮的,也是為了安全考慮使用,很多人看得懂Java程式碼但是看不懂arm指令,如果在Native層做了這麼多防護肯定效果會更好,當然如果你的應用為了更安全那就要考慮加固了,加固是最終方案了至於什麼加固廠商那就要自己選擇了,但是現在很多大公司不會考慮加固原因有兩個:

第一、現在沒有一個加固廠商跑出來說我們家的加固方案最牛逼,保證0崩潰率,只要有崩潰率那麼對於千萬級的應用也是很難忍受的,因為每天都有幾千甚至幾萬使用者手機的應用在崩潰會瘋的。

第二、現在加固廠商也就那麼幾家,用過加固的人或者沒用過用腚想想也知道,肯定需要上傳簽名檔案的,因為他加固最後還得重新簽名,最主要的是你把沒加固前的應用給他,你能保證他和我一樣很正直不會偷窺你的應用?不會在你的應用中加點什麼他們想要的?所以現在很多公司為了更安全點算了還是不去找加固廠商了。本來還安全點結果弄得不安全了。

現在也有很多公司已經在投入人力物力財力自己做加固,就是自己公司開發一套加固方案然後用於自己公司的所有產品,這樣肯定是最安全的了,但是這個前提是人力物力財力的投入,如果小公司那肯定是不會考慮的。所以加固本身來說技術肯定是最高階的也是最終的方案的。但是也不是說百分百安全,因為這個世界上還有一個完美的動作叫做脫殼。


更多安全內容討論,長按下方二維碼可以進入編碼美麗小密圈一起交流技術

640?wx_fmt=png


七、總結

所以本文說了很多方案,不可否認如果用了這些方案和沒用區別還是有的,本來想著是把這些方案寫成一個SDK開源的,但是最後想到沒時間沒精力弄就放棄了,但是我還是希望有熱愛動手的同學能夠按照這幾個方案寫一個完整的SDK出來也是造福人類光宗耀祖了。安全不息,逆向不止,生死看淡,不服就幹!


手機檢視文章不方便,可以網頁看

http://www.520monkey.com


《Android應用安全防護和逆向分析》 

 點選檢視圖書詳情

640?wx_fmt=jpeg

長按下面

相關文章