Android APK 加強–開發者角度(零)
1.APK加強介紹
Android應用程式使用的開發語言是Java語言,由于蠶蛹的是這種解釋性語言,是以代碼可以被反編譯。如果沒有經過混淆或是加密,會非常讓人容易讀懂,換言之就是容易被破解。為了能夠使我們的apk更好的保護起來,Android的開發以及安全人員對APK進行了一系列的加強措施。對APK加強從開發者的角度來看,以下是常用的手段和方式:
1.使用proguard對apk中的源碼進行混淆
2.對apk反編譯之後的smali進行混淆
3.對apk中的字元串進行加密
4.對apk中的檔案進行校驗
2.使用proguard對apk中的源碼進行混淆
AndroidStudio預設已經有proguard的支援,在小牧目錄安排app下的build.gradle中有配置資訊,隻需要将false變為true即可。
預設隻有release的配置,而工程預設是debug的,是以想要為debug上添加混淆要在release後面添加debug模式。

Proguard的混淆結果會處處到/build/outputs/mapping/release/目錄或是/build/outputs/mapping/debug/,一共有四個輸出檔案:
1.dump.txt:描述APK中所有類檔案的内部結構
2.mapping.txt:提供混淆前後類名,方法名和成員變量的對應關系。
3.seeds.txt:列出沒有被混淆的類和成員
4.usage.txt:列出從apk中移除的代碼
混淆之後可能出錯,出錯異常的分析:
http://simplyadvanced.net/blog/android-how-to-decode-proguards-obfuscated-code-from-stack-trace/
Proguard 包括四個功能,shrinker(壓縮),optimizer(優化),obfuscator(混淆),preverifier(預校驗)
3.對反編譯之後的smali進行混淆
源碼經過混淆之後,在一個産品級的apk中對apk的保護是有一定幫助的,而對于一些有一定經驗的破解者,他們還是會找到比較關鍵的地方,一般這種方式都是經過反編譯之後進行分析的,那麼如果我們效仿windows下的保護軟體,對smali代碼進行混淆,那麼某種程度上加大了破解者的分析難度。比較友善的混淆方式就是代碼亂序。
1.代碼亂序
原理:
我們在分析smali代碼時,一般會借助于反編譯工具反編譯成java代碼,代碼亂序的主要目的就是想要将smali代碼亂序之後,使反編譯的效果大打折扣,這樣就會擋住很短菜鳥,至少可以減慢分析速度。
2.對apk中的字元串進行加密
在開發過程中字元串的使用不可避免,但是這些字元串極可能是破解的關鍵點,比如伺服器的位址和錯誤提示這些敏感的字元串資訊。如果這些字元串采用寫死的方式,很容易通過靜态分析擷取。
1.采用StringBuilder拼接的方式。
2.編碼混淆:在寫死的時候将字元串先轉換成16進制的數組或者Unicode編碼,在使用的時候在回轉字元串,這種方式比StringBuider方式更難直接識别。
3.加密處理:先将字元串在本地進行加密處理,後将密文寫死進去,運作時再進行解密。可以将解密方法通過JNI實作。
4.對apk中的檔案進行校驗
在apk中包括代碼和資源以及簽名檔案,一般我們會對可執行檔案進行校驗,還有證書的簽名進行校驗,以及apk本身的校驗。
1. 對apk中的dex檔案進行校驗
classes.dex是android虛拟機的可執行檔案,我們所寫的java代碼騎士都在這裡面,所有很多賭赢程式的篡改都是針對classes.dex檔案的。
1.編寫代碼
代碼比較簡單,這裡是通過計算好的crc儲存在string.xml檔案裡,當然我們可以随便那一個值代替,等開發完成後替換掉。
private void verifyDex(){
Long 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");
//計算classex.dex的crc
long dexEntryCrc = dexEntry.getCrc();
Log.d("DEX",dexEntryCrc+" ");
//對比
if(dexCrc == dexEntryCrc){
Log.d("DEX","dex hasn't been modified");
}else{
Log.d("DEX","dex has ben modified");
}
}catch (IOException e){
e.printStackTrace();
}
}
2.對apk中的apk進行校驗
與DEX校驗不同apk校驗必須把計算好的Hash值放在網絡服務端,因為對APK的任何改動都會印象到最後的Hash值。
校驗代碼:
private void verifyApk(){
//擷取data/app/**/base.apk 路徑
String apkPath = getPackageResourcePath();
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 bInt = new BigInteger(1,msgDigest.digest);
String md5 = bInt.toString(16;
fis.close();
Log.d("apk",md5);
//擷取服務端的md5對比
}catch(){
}
3.對apk中的簽名進行校驗
每個APK都會經過開發者獨有的整數進行簽名,如果破解者對APK進行二次打包一般會用自己的簽名證書 打包。這時候我們就可以通過校驗簽名證書的MD5值進行校驗。
校驗代碼:
public void verifySignature(){
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值
//擷取服務端簽名證書,對比