Android 防破解技術簡介
這幾年隨着互聯網的不斷發展,Android App 也越來越多!但是隨之而來的問題也越來越多,這其中比較令人頭疼的問題就是:有些不法分子利用反編譯技術破解 App,修改代碼,之后再重新編譯上傳到應用商店!到時候,如果用戶下載了這些應用,就可能造成個人信息或者金錢的丟失!所以,作為開發者的我們,有必要學習一些防止被破解的技術手段了!
簡介
現在想要破解一個 App, 大多數都是遵循着以下的幾個步驟:
- 反編譯 App
- 查看 App 源代碼並修改
- 動態調試(有可能存在而已,畢竟修改源碼之后也是要調試的,哈)
- 重新編譯 App
- 上傳破解的 App 到商店中
目前來講大多數的破解只存在一二兩步中,但是后面的幾個步驟還是會有人弄的,所以我們不能掉以輕心呀!當然最后一步我們姑且不談了!下面我就來一個一個的介紹防止這些步驟的方法!
防止反編譯 App
目前來說,大多數人用的反編譯工具都是:jdax,dex2jar,jd-gui等工具,但是對於我們來說有個好消息就是 這些工具很多都是開源的,我們可以在 Github
上找到這些工具的源碼,我們可以通過閱讀這些源碼,從而找出防止他們反編譯的方法,也就是讓他在反編譯你的 App 的時候,讓他們的軟件崩潰!對於這個我們可以忽略,因為去閱讀這些代碼並理解他們,是一項比較耗時的工作甚至有可能都讀不通他們的程序,所以我們盡量把經歷放在下面的幾個步驟上!
防止查看源代碼
其實並不能真正的做到防止別人查看我們的源代碼,我們只是通過代碼混淆的技術來使別人在查閱我們的源代碼時變得更加的困難而已。在 Android 中我們可以使用 ProGuard 來混淆我們的代碼。在這里先說明一下代碼混淆的優缺點:
優點:
- 增加別人破解 app 的難度
- 壓縮、優化、刪除代碼
- 通過刪除代碼功能實現的特殊作用
缺點:
- 增加我們自己的調試難度(相信混效過代碼的同學都知道,在混淆之后,app會出各種錯誤)
- 反射方面的問題(就是找不到對應的類)
對於第一個缺點的問題的解決方案是:保存 mappinp.txt 這個保存映射關系的文件,當 app 在混淆運行之后可能出現的問題,可以根據這個映射關系來快速定位出 bug 所在的位置!對於第二個缺點的問題的解決方案是:不混淆該類,如果不知道是哪個類那只能運行的時候調試了!
接下來簡要介紹一下 ProGuard 的常用語法:
-libraryjars class_path 應用的依賴包,如android-support-v4
-keep [,modifier,...] class_specification 不混淆某些類
-keepclassmembers [,modifier,...] class_specification 不混淆類的成員
-keepclasseswithmembers [,modifier,...] class_specification 不混淆類及其成員
-keepnames class_specification 不混淆類及其成員名
-keepclassmembernames class_specification 不混淆類的成員名
-keepclasseswithmembernames class_specification 不混淆類及其成員名
-assumenosideeffects class_specification 假設調用不產生任何影響,在proguard代碼優化時會將該調用remove掉。如system.out.println和Log.v等等
-dontwarn [class_filter] 不提示warnning
在這里在給出 android 中標准的 proguard.cfg
文件內容
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
大家可以依據此模版在進行增刪改查。另外,我還可以通過代碼混淆來控制是否打印日志輸出:
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
在我寫此文章是,有些牛逼的公司已經可以進行資源文件名的混淆了,這樣就更加增大了代碼閱讀難度了!在這里給出該文章:美團Android資源混淆保護實踐
防止動態調試
所謂的動態調試就是使用 檢測調試器
檢測調試器
這個檢測很簡單:就是當我們的軟件檢測到連接到調試器的時候,就終止程序!
首先,我們在 AndroidManifest.xml
中的 Application
標簽中添加 android:debuggable="false"
這個屬性,使程序不可調試,如果有人想調試,必定要修改這個值,之后我們只要在代碼中添加下面短代碼來檢測該值是否變改變過,如果改變過就停止程序。
if((getApplicationInfo().flags &=
ApplicationInfo.FLAG_DEBUGGABL)!=0){
android.os.Process.killProcess(android.os.Process.myPid());
}
這樣就可以解決這個問題了
防止重新編譯
代碼在上傳的時候是需要對 app 進行的簽名的,而每個簽名文件只有 app 開發者才有,所以我們可以根據簽名的內容來進行對比。但是簽名的文件過長不利於對比,我們用見面簽名內容的 hashCode
來比較。獲取這個值的代碼:
public int getsignature(String packageName) {
try {
android.content.pm.Signature[] signatures= getPackageManager().getPackageInfo(packageName.toString(), PackageManager.GET_SIGNATURES).signatures;
return signatures[0].hashCode();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return 0;
}
}
我們只要在本地打包之后獲取到這個值,之后在每次程序運行的時候,在獲取簽名的 hashCode
的來與我們之前在本地打包的值進行比較就行了!如果不同就直接關閉程序就行了!
NDK保護
就是關鍵代碼使用 C/C++ 代碼來寫,沒有研究過
加殼
在現有的代碼外加一層 C/C++ 代碼來保護,也沒有研究過
參考資料
- Android 軟件安全與逆向分析