日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

017 Android加固之APK混淆和保护方式

發布時間:2025/3/21 Android 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 017 Android加固之APK混淆和保护方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • APK加固介紹
      • 使用proguard對apk中的源碼進行混淆
        • proguard原理
      • 對APK反編譯之后的smali進行混淆
        • 代碼亂序原理
        • 亂序實例-Hello
      • 對APK中的字符串進行加密
        • 解決方案1-StringBuilder拼接
        • 解決方案2-編碼混淆
        • 解決方案3-加密處理
      • 小結
      • 對APK中的文件進行校驗
        • 對APK中的Dex文件進行校驗
        • 對APK中的APK進行校驗
        • 對APK中的簽名進行校驗
        • 小結

APK加固介紹

Android應用程序使用的開發語言是JAVA,由于采用的是這種解釋型語言,所以代碼可以被反編譯。如果沒有經過混淆或是加密,會非常容易讓人分析。

為了使我們的APK更好的保護起來,Android的開發以及安全人員對APK進行了一系列的加固措施。從開發者而言,以下是常用的手段和方式:

  • 使用proguard對apk中的源碼進行混淆
  • 對APK反編譯之后的smali進行混淆
  • 對APK中的字符串進行加密
  • 對APK中的文件進行校驗
  • 使用proguard對apk中的源碼進行混淆

    Android Studio默認已經有proguard的支持,在項目目錄app下的build.gradle中有配置信息,只需要將false改成true即可

    默認只有release的配置,而工程默認是debug的,所以想要debug添加代碼混淆,需要在release后面添加debug模式

    編譯以后可以看到混淆的效果

    上面都是系統的一些類庫,被混淆成了無意義的字符

    反編譯后的源碼可以看到對類名和系統庫進行了一定程度的混淆。

    Proguard的混淆結果會輸出到<module-name>/build/outputs/mapping/release/目錄或是``/build/outputs/mapping/debug/`

    一共有四個輸出文件

  • dump.txt:描述APK中所有類文件的內部結構
  • mapping.txt:提供混淆前后的類名,方法名和成員變量名的對應關系
  • seeds.txt:列出沒有被混淆的類和成員
  • usage.txt:列出從APK中移出的代碼
  • proguard原理

    proguard包括四個功能,shrinker(壓縮),optimizer(優化),obfuscator(混淆),preverifier(預校驗)

    • shrinker(壓縮):檢測并移除沒有用到的類,變量,方法和屬性
    • optimizer(優化):優化代碼,非入口節點類會加上private/static/final,沒有用到的參數會被刪除,一些方法可能會變成內聯代碼
    • obfuscator(混淆):使用短又沒有語義的名字重命名非入口類的類名,變量名和方法名。入口類的類名保持不變
    • preverify(預校驗):預校驗代碼是否符合Javal.6或者更高的規范

    一般用戶可以選擇默認的設置即可,如果想要自定義可以在proguard-rules.pro文件中進行配置 。

    對APK反編譯之后的smali進行混淆

    源碼經過混淆之后,在一個產品級的APK中對APK的保護是有一定幫助的,而對于一些有經驗的破解者,他們還是會找到比較關鍵的地方,一般這種方式都是反編譯之后進行分析的,那么如果我們效仿Windows下的保護軟件,對smali代碼進行混淆,某種程度上就加大了破解者的分析難度。比較方便的混淆方式就是代碼亂序

    代碼亂序原理

    我們在分析smali代碼時,一般會借助于反編譯工具反編譯成java代碼。代碼亂序的目的就是想要將smali代碼亂序后,使反編譯的效果大打折扣,這樣就會擋住很多菜鳥,至少可以減慢分析速度,增加破解難度。

    亂序的基本原理如上圖所示,將指令重新布局,并給每塊指令賦予一個 label,在函數開頭處使用 goto 跳到原先的第一條指令處,然后第一條指令處理完,再跳到 第二條指令,以此類推。

    亂序實例-Hello

    我們先從最簡單的Hello開始

    public class Hello{public static void main(String[] argc){String a="1";String b="2";String c=a+b;System.out.println(c);} }

    首先用Android Studio編譯成APK,然后放到Android Killer中反編譯

    接著來修改smali代碼

    我們可以大致把整個代碼分成三部分:

  • 定義變量
  • 字符串拼接
  • 打印字符串
  • 根據這三部分,我們可以根據上面的原理對代碼中的三部分進行亂序,加上標簽跟跳轉

    亂序后的代碼如下

    復制一份smali副本

    將沒有用的smali代碼全部刪除,只保留Hello.smali

    先用smali.jar將整個文件夾轉成dex文件,接著用dex2jar將dex文件轉成jar文件

    此時再來查看jd-gui,可以看到這個工具的反編譯效果大打折扣

    但是對jadx實際上是無效的,畢竟這個工具的實在是太強大了 。

    同時proguard一個東家的dexguard,最初就采取類似的方法對代碼進行保護,當然除了這種保護方式外,還有其他的,比如最常見的就是字符串加密。

    對APK中的字符串進行加密

    在開發過程中字符串的使用是不可避免的,但是這些字符串極可能是破解的關鍵點,比如服務器的地址和錯誤提示這些敏感的字符串信息。如果這些字符串采用硬編碼的方式很容易通過靜態分析獲取

    在java中定義一個字符串

    public String normalString(){String str="Hello wordl";return str; }

    反編譯的smali代碼

    .method public normalString()Ljava/lang/String;const-string v0, "Hello wordl".local v0, "str":Ljava/lang/String;return-object v0 .end method

    可以看出const-string關鍵字后面就是定義的字符串值,甚至可以使用自動化分析工具批量提取出來

    解決方案1-StringBuilder拼接

    StringBuilder類通過append方法來構造需要的字符串,這種方式可以增加自動化分析的難度,如果要獲取完整的字符串就必須進行相應的語法解析了

    Java中拼接字符串代碼

    public String buildString(){StringBuilder builder=new StringBuilder();builder.append("Hello");builder.append(" ");builder.append("wordl");return builder.toString(); }

    反編譯的smali代碼

    .method public buildString()Ljava/lang/String;.locals 2.line 18new-instance v0, Ljava/lang/StringBuilder;invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V.line 19.local v0, "builder":Ljava/lang/StringBuilder;const-string v1, "Hello"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 20const-string v1, " "invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 21const-string v1, "wordl"invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;.line 22invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;move-result-object v1return-object v1 .end method

    可以看出反編譯后的smali代碼對破解增加了一定的難度,并不能一眼就識別出來

    解決方案2-編碼混淆

    編碼混淆是在硬編碼的時候將字符串轉換成16進制的數組或者Unicode編碼,在使用的時候轉回字符串。這種方式在反編譯smali代碼比StringBuilder方式更難直接識別

    Java代碼

    public String encodingString() {byte[] strBytes = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6f, 0x72, 0x6C, 0x64};String str = new String(strBytes);return str; }

    反編譯的smali代碼

    .method public encodingString()Ljava/lang/String;.locals 2.line 26const/16 v0, 0xbnew-array v0, v0, [Bfill-array-data v0, :array_0.line 27.local v0, "strBytes":[Bnew-instance v1, Ljava/lang/String;invoke-direct {v1, v0}, Ljava/lang/String;-><init>([B)V.line 28.local v1, "str":Ljava/lang/String;return-object v1nop:array_0.array-data 10x48t0x65t0x6ct0x6ct0x6ft0x20t0x77t0x6ft0x72t0x6ct0x64t.end array-data .end method

    解決方案3-加密處理

    加密處理是先將字符串在本地進行加密處理,后將密文硬編碼進去,運行時再進行解密。加密步驟:

  • 字符串加密
  • 硬編碼進程序
  • 編譯運行
  • 解密密文
  • 當然因為Java代碼相對來說比較容易反編譯,并且該方式需要將解密方法放在APK本地,所以我們可以將解密方法通過JNI實現,加大反編譯難度

    小結

    任何一種加固方式都只是加大了破解的難度,并不能完全避免Android程序被破解。

    對APK中的文件進行校驗

    在APK中包括代碼和資源以及簽名文件,我們一般會對可執行文件進行校驗,還有證書的簽名進行校驗,以及apk本身的校驗。

    對APK中的Dex文件進行校驗

    classes.dex是Android虛擬機的可執行文件,我們所寫的JAVA代碼其實都在這里,很多應用程序的篡改都是針對classes.dex的。

    代碼比較簡單,這里是通過計算好的CRC保存在string.xml里。本例可以直接運行代碼校驗,在監視器中查看到CRC的值,然后修改string.xml中對應的值即可。

    校驗代碼:

    private void VarifyDex(){//獲取string.xml的valueLong dexCrc=Long.parseLong(this.getString(R.string.crc_value));String apkPath=this.getPackageCodePath();try {ZipFile zipFile=new ZipFile(apkPath);ZipEntry dexEntry=zipFile.getEntry("classes.dex");//計算class.dex的CRC值long dexEntryCrc=dexEntry.getCrc();//對比if (dexCrc==dexEntryCrc){Log.d("Dex","dex not been modify");}else{Log.d("Dex","dex has been modify");}}catch (IOException e){e.printStackTrace();} }

    對APK中的APK進行校驗

    與Dex校驗不同,APK校驗必須把計算好的哈希值放在網絡服務端,因為對APK的任何改動都會影響到最后的哈希值。

    校驗代碼:

    private void VarifyApk() {//獲取data/app/***/base.apkString apkPath=getPackageResourcePath();Log.d("GuiShou","apkPath:"+apkPath);MessageDigest msgDigest;try {//獲取apk并計算MD5值msgDigest=MessageDigest.getInstance("MD5");byte[] bytes=new byte[4096];int count;FileInputStream fis;fis=new FileInputStream(new File(apkPath));while ((count=fis.read(bytes))>0){msgDigest.update(bytes,0,count);}//計算出md5值BigInteger bigInteger=new BigInteger(1,msgDigest.digest());String md5=bigInteger.toString(16);fis.close();Log.d("GuiShou","md5:"+md5);}catch (IOException e){e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}//獲取服務端的md5進行對比.....}

    對APK中的簽名進行校驗

    每個APK都會經過開發者獨有的證書進行簽名,如果破解者對APK進行二次打包一般會用自己的簽名證書進行打包。這時我們就可以通過校驗簽名證書的MD5值進行校驗

    校驗代碼

    public void VerifySignatrue(){String packageName=this.getPackageName();PackageManager pm=this.getPackageManager();PackageInfo pi;String md5="";try {pi=pm.getPackageInfo(packageName,PackageManager.GET_SIGNATURES);Signature[] s=pi.signatures;//計算出MD5值MessageDigest messageDigest=MessageDigest.getInstance("MD5");messageDigest.reset();messageDigest.update(s[0].toByteArray());BigInteger bigInteger=new BigInteger(1,messageDigest.digest());md5=bigInteger.toString();} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}//---獲取服務端的簽名證書進行對比---}

    獲取簽名證書md5可以解壓APK,找到META-INF文件夾中的CERT.RSA,通過keytool工具就可以看到其中的md5等屬性

    小結

    當然上述的保護方式容易被暴力破解,完整性檢查最終還是通過返回true/false來控制后續代碼邏輯的走向。如果攻擊者直接修改代碼邏輯,使完整性檢查始終返回true,那這種方法就無效了,所以類似文件完整性的校驗需要配合一些其他的方法,或者更為巧妙的方法實現。

    總結

    以上是生活随笔為你收集整理的017 Android加固之APK混淆和保护方式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。