怎樣制作一個防止重打包的APK【反脫殼反HOOK】
本文討論的重打包,是指“破解版”,運行環境受(普通用戶的手機)限制,畢竟你不能要求用戶魔改刷機root才能使用“破解版”。另:沒有永遠的安全,全看cracker的水平有多高。
APK需要被重打包,勢必是為了修改其中的代碼,可能是dex,可能是so,可能是dll(unity3d),也可能是某個腳本等等,重打包后簽名(未泄露)就會被改變。
不管怎樣,可以確定的是簽名和其中的某些文件會被修改。那么防止重打包的主要目標就是檢測這些修改。
在保護之前,先來說說cracker可能會干的幾件事:
1、爆破。直接修改代碼,常見于純java編寫的或者簡單的so校驗。
2、脫殼。如果方法1不行,那可能是APK被加固了,所以可以考慮脫殼后再修改。局限性很大,困為很多殼脫出來沒法直接打包運行。但是脫殼可以了解程序邏輯,為方法3做準備。
3、內存補丁。如果方法2不行,那就不脫殼了,根據程序邏輯,可以直接加載自己編寫的so打內存補丁跳過檢測。
4、hook。這可能是最近幾年用得最多的方案了。上面所有方法試了不行(或者懶得試了),就要走上hook這條路了。
針對以上,可以做的事:
1、針對爆破。這沒啥好說的,把校驗寫進so,復雜一點,花里胡哨一點,再弄個ollvm,或者加個殼。
2、針對脫殼。上文就說了,很多殼脫出來直接打包是不行的,也有些殼是脫不了的,比如java轉c(保護java代碼),vmp(保護so),加殼之后原指令徹底消失不見,也就不存在脫殼的說法了。
3、針對內存補丁。這個方法其實很冷門,因為內存補丁對逆向要求較高,有能力讀懂ollvm和加殼后的so的邏輯的,基本很難阻止他做事了。
4、針對hook:作為最熱門的方法,有必要單獨拎出來多說幾句。
a)檢測hook。方法太多了,各種hook都有或多或少的特征,要注意的是,會有諸如《通過hook繞過hook檢測》之類的文章……今天你hook我,明天我檢測你,后天你跳過我的檢測,大后天我檢測你跳過我的檢測……對抗永無止境。
b)不被hook。怎樣不被hook?自己寫函數。用java代碼來舉個例子,有一個bytes數據要計算它的md5,最簡單的MessageDigest.getInstance("MD5").digest就完事了,如果調用了公共庫,那么就容易被hook住,然后就bytes數據暴露了,計算結果也被修改了。所以多用原生類型自己寫函數吧。
看了這么多文字,仿佛啥也沒明白,沒關系,下面說一下我做的事:
1、使用svc指令讀取文件。能讀取到真實的APK,就能檢測APK的完整性。能讀取到真實的maps就能檢測到各種hook框架的特征。能讀取到真實的/proc/下的各種文件,就能檢測各種調試器,各種cracker留下的痕跡。
2、保護svc指令。svc指令只能通過inlinehook修改內存實現(內核hook不在本文討論范圍內,本文開頭就說明了)。如果cracker在搜索內存時,沒有搜索到svc指令,那么我們執行代碼時,就不會被hook住。
下面才是真正做的事:
1、對某些關鍵的邏輯(如果邏輯是用java寫的,可以先套java轉C殼)加一個殼。
2、這個殼的靜態代碼中不存在任何svc特征,一旦加載so,先進行完整性校驗,當校驗需要讀取APK時,釋放svc代碼執行讀取操作,這樣在內存搜索不到svc,也就無法被hook。如果要校驗maps中是否有hook框架也在這里進行。
3、當校驗通過,再執行真正的程序邏輯,并且此時原程序邏輯的so是碎片化的(就像java的指令抽取),此時從內存dump出來的so也是不完整的,所以無法脫殼出原so。
以上,實現了防爆破(加殼無法修改),防脫殼(so分解了,無法脫殼),防hook(svc即插即用,無法hook,除非改內核)。然后,就只有內存補丁一條路了,再想想魔改的ollvm規則,算了。