最近抽空研究了下android 加殼技術(shù),發(fā)現(xiàn)關(guān)于加殼的源代碼特別少,即使有也不能做到版本兼容,問題又特別多,對app加殼感覺遠(yuǎn)沒有當(dāng)初想的那么簡單,而現(xiàn)階段成熟的加密軟件多是收費(fèi)的,愛加密,梆梆加固等。收費(fèi)就暫不考慮了,咱們只能曲線救國,也試過騰訊御安全,認(rèn)證通過了,也能查看安全報告,就是無法加固,試了很久一直再刷新,最終也沒有加固成功;試過網(wǎng)易云易盾,只是申請一下試用,打了幾個電話做廣告,最終也沒有同意申請。第三方加密平臺沒有money看來是行不通了!

我們都知道,常見的APP加密方法包括偽加密、混淆、運(yùn)行驗證和第三方加密平臺APP加密。
偽加密
偽加密是Android4.2.x系統(tǒng)發(fā)布前的加密方式之一,通過java代碼對APK(壓縮文件)進(jìn)行偽加密,其修改原理是修改連續(xù)4位字節(jié)標(biāo)記為”P K 01 02”的后第5位字節(jié),奇數(shù)表示不加密偶數(shù)表示加密。雖然偽加密可以起到一定防破解作用,但也會出現(xiàn)問題,首先使用偽加密對其APK加密后市場無法對其進(jìn)行安全檢測,導(dǎo)致部分市場會拒絕這類APK上傳;其次,偽加密的加密方式和解密方式也早已公布導(dǎo)致它的安全程度也大大降低;再次,Android4.2.x系統(tǒng)無法安裝偽加密的APK;最后偽加密只是對APK做簡單保護(hù),在java層源碼加殼保護(hù)、核心so庫、資源文件、主配文件、第三方架包方面卻沒有任何保護(hù)處理。
注意:高版本不支持這樣的方法,所以還是不要嘗試使用這樣的加密方式了?,F(xiàn)在Android 版本系統(tǒng)一般都是4.4以上的了,高版本也不支持,pass掉該加密方式。
驗證
APP加密之運(yùn)行時驗證,主要是指在代碼啟動的時候本地獲取簽名信息然后對簽名信息進(jìn)行檢驗來判斷自己的應(yīng)用是否是正版,如果簽名信息不是正版則提示盜版或者直接崩潰。當(dāng)然你可以把必要的數(shù)據(jù)放在服務(wù)器端。破解:找到smali文件中,判斷是否相等的部分。改為常量true,即失效??傊淳幾g一些apk之后,只要是java代碼寫的總會有smil文件。對于smil文件,如果耐心讀的話,還是可以查看到一些關(guān)鍵代碼的。
混淆
APP加密之混淆,混淆是把原來有具體含義的類名,變量名,方法名,修改成讓人看不懂的名字,例如方法名getUserName編程了方法名。代碼混淆只是增加APP代碼的閱讀難度,對APP安全起不到實質(zhì)的作用,但其是APP加密之前的一個必要步驟。
個人覺得混淆還是比較有用的,加密之前提供初步的保護(hù),就稍微研究了一下。
初步配置
大致:構(gòu)建類型有debug 和release版本,我們在發(fā)布版本的時候指定 minifyEnabled=true; 設(shè)置啟用代碼混淆,混淆規(guī)則記錄在proguard-rules.pro這個文件中,
buildTypes{
release{
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}
}
指定混淆規(guī)則
混淆規(guī)則是用來指定是否混淆的規(guī)則,聽起來像廢話,在這個proguard-rules.pro中我們需要指定不需要混淆的類。哪些不需要混淆呢,我們分為基本指令區(qū)和定制指令區(qū),基本指令區(qū)包括如下部分:
#-----------------------------基本指令區(qū)-------------------------------------------------------------
-optimizationpasses5 ? ? ?#指定代碼的壓縮級別0-7
-dontusemixedcaseclassnames ? ?#是否使用大小寫混合
-dontpreverify ? ? ? ? ?#混淆時是否做預(yù)校驗
-verbose# ? ? ? ?混淆時是否記錄日志
-optimizations!code/simplification/arithmetic,!field/*,!class/merging/* ? ? ?#混淆時所采用的算法
-printmapping ? ? proguardMapping.txt
-keepattributes *Annotation*,InnerClasses
-keepattributes ?Signature
-keepattributes ?SourceFile,LineNumberTable
#-------------------------四大組件默認(rèn)保留區(qū)---------------------------------------------------------
-keeppublic class * extends android.app.Activity
-keeppublic class * extends android.app.Application
-keeppublic class * extends android.app.Service
-keeppublic class * extends android.content.BroadcastReceiver
-keeppublic class * extends android.content.ContentProvider
-keeppublic class * extends android.app.backup.BackupAgentHelper
-keeppublic class * extends android.preference.Preference
-keeppublic class com.android.vending.licensing.ILicensingService
-keepclass android.support.** {*;}
#------------------------------------------------------------------------------------------------
-keepclasseswithmembernamesclass * {
native ;
}
-keepclasseswithmembersclass * {
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembersclass * {
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembersclass * extends android.app.Activity {
public void *(android.view.View);
}
#---------------------------------------------------------------------------------------------------
-keepclassmembersenum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keeppublic class * extends android.view.View{
*** get*();
void set*(***);
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembersclass * {
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclass * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembersclass * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keepclass **.R$* {
*;
}
-keepclassmembersclass * {
void *(**On*Event);
}
#---------------------------------webview------------------------------------
-keepclassmembersclass fqcn.of.javascript.interface.for.Webview {
public *;
}
-keepclassmembersclass * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembersclass * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
-keep 顧名思義 :保留不需要混淆。除了基本指令區(qū)的需要保留,我們還有定制指令區(qū)需要保留,例如:實體類,json,webview,反射相關(guān)的類和方法,第三方j(luò)ar文件,與js互相調(diào)用的類等。
//實體類 :保留該包下的類及子類,子類中方法等不混淆
-keep public class 包名.**{*;}
#zxing-第三方j(luò)ar包 保留不被混淆
-libraryjars libs/zxing.jar
-dontwarn com.google.zxing.**
-keep class com.google.zxing.**{*;}
//so文件保留不被混淆,一般編譯過程中會自動忽略混淆。不需要單獨(dú)列出。
-libraryjars libs/x86/liblocSDK7a.so
注意:在此過程中盡可能剔除所有不需要混淆的類,避免不必要的閃退。
運(yùn)行時注意在release模式下,進(jìn)行正常的簽名打包。代碼混淆過之后可以反編譯一下,看看效果,如果暴露了一些重要信息,可再次修改proguard-rules.pro文件混淆重要信息。
對于第三方j(luò)ar,或者依賴的項目,不需要混淆,通用寫法參考如下:
-dontwarn 主要是避免警告,-keep 主要是保留不被混淆

混淆后崩潰調(diào)試
通常情況下,寫好混淆代碼以后安裝release版本的apk到手機(jī)有時根本運(yùn)行不起來,有時啟動了瞬間崩潰,其實原因很簡單我們應(yīng)用的庫或者第三方j(luò)ar被混淆了導(dǎo)致無法正常調(diào)用,那怎么查找是哪些不該混淆了的被混淆了?
在Android studio 中生成release包的同時 build\outputs\mapping\release文件夾下也生成了4個文件
分別有以下文件:
+ dump.txt 描述apk文件中所有類文件間的內(nèi)部結(jié)構(gòu)。
+ mapping.txt 列出了原始的類,方法,和字段名與混淆后代碼之間的映射。
+ seeds.txt 列出了未被混淆的類和成員
+ usage.txt 列出了從apk中刪除的代碼

一般情況下,我們直接看mapping 和seeds這2個文件夾就可以了,在我調(diào)試過程中,使用notedpad打開mapping文件,將近12萬行,怪不得用txt打開瞬間崩潰卡死。
mapping文件記錄了所有的混淆前后的映射關(guān)系,告訴你混淆前后,據(jù)我分析,混淆后文件會變成abc,有些庫你可能忽略,但是mapping會記錄所有,你會發(fā)現(xiàn) 不改映射的第三方包 也變成abc了,這里就是出錯的根本,不該混淆的被混淆了,一一找出,在proguard-rules.pro中-dontwarn 包名,-keep class 包名 就可以順利解決。12萬行并不是讓你一行一行去看的,點擊對應(yīng)包的關(guān)鍵字滾動查看。一一過濾。
seeds文件夾是幫你查看是否需要混淆的類沒有被混淆,當(dāng)然都是可以修改的,查看過程中會有errormessage,搜索一下,沒有最好,有了解決掉,也許不應(yīng)該混淆的被你混淆掉。
混淆過后,app順利運(yùn)行,這種感覺超爽。
最終檢驗
反編譯一下看看是否成功混淆?
我們需要工具dex2jar-2.0, jd-gui-windows-1.4.0,還需要一個apk 文件,dapp.apk,首先重命名為dapp.zip,解壓縮得到dapp文件夾,得到dapp文件下的dex文件,將dex文件復(fù)制到dex2jar-2.0文件夾下。按下shift鍵的同時點擊鼠標(biāo)右鍵 打開命令窗口 ,在命令窗口中輸入d2j-dex2jar.bat classes.dex 命令即可生成classes-dex2jar.jar文件。用jd-jui打開即可查看混淆后的代碼。





至此就大功告成了,簡單的代碼混淆和反編譯真的沒有想象中那么難!