Android安全-代碼安全3-Dex文件校驗


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 block

13. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM