標籤:
Android安全-代碼安全3-Dex檔案校正
重編譯apk其實就是重編譯了classes.dex檔案,重編譯後,產生的classes.dex檔案的hash值就改變了,因此我們可以通過檢測安裝後classes.dex檔案的hash值來判斷apk是否被重打包過。
(1)讀取應用安裝目錄下/data/app/xxx.apk中的classes.dex檔案並計算其雜湊值,將該值與軟體發布時的classes.dex雜湊值做比較來判斷用戶端是否被篡改。
(2)讀取應用安裝目錄下/data/app/xxx.apk中的META-INF目錄下的MANIFEST.MF檔案,該檔案詳細記錄了apk包中所有檔案的雜湊值,因此可以讀取該檔案擷取到classes.dex檔案對應的雜湊值,將該值與軟體發布時的classes.dex雜湊值做比較就可以判斷用戶端是否被篡改。
為了防止被破解,軟體發布時的classes.dex雜湊值應該存放在伺服器端。
另外由於逆向c/c++代碼要比逆向Java代碼困難很多,所以關鍵代碼部位應該使用Native C/C++來編寫。
classes.dex 在 Android 系統上基本負責完成所有的邏輯業務,因此很多針對 Android 應用
程式的篡改都是針對 classes.dex 檔案的。在 APK 的自我保護上,也可以考慮對 classes.dex
檔案進行完整性校正,簡單的可以通過 CRC 校正完成,也可以檢查 Hash 值。由於只是檢查
classes.dex,所以可以將 CRC 值儲存在 string 資源檔中,當然也可以放在自己的伺服器上,
通過運行時從伺服器擷取校正值。基本步驟如下:
- 首先在代碼中完成校正值比對的邏輯,此部分代碼後續不能再改變,否則 CRC 值
會發生變化;
- 從產生的 APK 檔案中提取出 classes.dex 檔案,計算其 CRC 值,其他 hash 值類似;
- 將計算出的值放入 strings.xml 檔案中。
核心代碼如下:
代碼:
1. String apkPath = this.getPackageCodePath();2. Long dexCrc = Long.parseLong(this.getString(R.string.dex_crc));3. try {4. ZipFile zipfile = new ZipFile(apkPath);5. ZipEntry dexentry = zipfile.getEntry("classes.dex");6. if(dexentry.getCrc() != dexCrc){7. System.out.println("Dex has been *modified!");8. }else{9. System.out.println("Dex hasn‘t been modified!");10. }11. } catch (IOException e) {12. // TODO Auto-generated catch block13. e.printStackTrace();14. }
但是上述的保護方式容易被暴力破解, 完整性檢查最終還是通過返回 true/false 來控制
後續代碼邏輯的走向,如果攻擊者直接修改代碼邏輯,完整性檢查始終返回 true,那這種方
法就無效了,所以類似檔案完整性校正需要配合一些其他方法,或者有其他更為巧妙的方式
實現?
APK 完整性校正
雖然 Android 程式的主要邏輯通過 classes.dex 檔案執行,但是其他檔案也會影響到整個
程式的邏輯走向,以上述 Dex 檔案校正為例,如果程式依賴 strings.xml 檔案中的某些值,則
修改這些值就會影響程式的運行,所以進一步可以整個 APK 檔案進行完整性校正。但是如
果對整個 APK 檔案進行完整性校正,由於在開發 Android 應用程式時,無法知道完整 APK 文
件的 Hash 值,所以這個 Hash 值的儲存無法像 Dex 完整性校正那樣放在 strings.xml 檔案中,
所以可以考慮將值放在伺服器端。核心代碼如下:
代碼:
1. MessageDigest msgDigest = null;2. try {3. msgDigest = MessageDigest.getInstance("MD5")4. byte[] bytes = new byte[8192];5. int byteCount;6. FileInputStream fis = null;7. fis = new FileInputStream(new File(apkPath));8. while ((byteCount = fis.read(bytes)) > 0)9. msgDigest.update(bytes, 0, byteCount);10. BigInteger bi = new BigInteger(1, msgDigest.digest());11. String md5 = bi.toString(16);12. fis.close();13. /*14. 從伺服器擷取儲存的 Hash 值,並進行比較15. */ 16. } catch (Exception e) {17. e.printStackTrace();18. }
轉自:http://bbs.pediy.com/showthread.php?t=183116
Android安全-代碼安全3-Dex檔案校正