你發佈的Android 應用安全嗎?

前言

你們都知道,目前大多數的Android 是用Java語言寫的,即便如今Google很是力薦kotlin,可是還有目前不少的項目仍是使用Java編寫,畢竟一個語言的替換是須要時間。所以,Java代碼容易被反編譯也是總所周知的,所以本身的防止被反編譯仍是須要重視的。java

主要從四個方面來介紹本文:android

1.Proguard混淆算法

2.對抗反編譯工具sql

3.對抗安卓模擬器bash

4.對抗apk重打包app

Proguard混淆

Proguard 基礎ide

Proguard 是一個混淆代碼的開源項目,Proguard主要的做用是混淆,固然他還有着對字節碼進行縮減體積、優化等功能,我主要關注的是混淆。工具

概念大數據

下面兩張圖片分別是混淆了和沒有混淆的圖片(網上找的圖):優化

這樣能夠看出來,假如混淆和沒有假如混淆的區別之大。使用混淆以後的類名就徹底變了,天然假如混淆後,針對反編譯仍是有效果。

基本語法:

-include {filename}    從給定的文件中讀取配置參數   
-basedirectory {directoryname}    指定基礎目錄爲之後相對的檔案名稱   
-injars {class_path}    指定要處理的應用程序jar,war,ear和目錄   
-outjars {class_path}    指定處理完後要輸出的jar,war,ear和目錄的名稱   
-libraryjars {classpath}    指定要處理的應用程序jar,war,ear和目錄所須要的程序庫文件   
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的庫類。   
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可見的庫類的成員。  


保留選項   
-keep {Modifier} {class_specification}    保護指定的類文件和類的成員   
-keepclassmembers {modifier} {class_specification}    保護指定類的成員,若是此類受到保護他們會保護的更好  
-keepclasseswithmembers {class_specification}    保護指定的類和類的成員,但條件是全部指定的類和類成員是要存在。   
-keepnames {class_specification}    保護指定的類和類的成員的名稱(若是他們不會壓縮步驟中刪除)   
-keepclassmembernames {class_specification}    保護指定的類的成員的名稱(若是他們不會壓縮步驟中刪除)   
-keepclasseswithmembernames {class_specification}    保護指定的類和類的成員的名稱,若是全部指定的類成員出席(在壓縮步驟以後)   
-printseeds {filename}    列出類和類的成員-keep選項的清單,標準輸出到給定的文件   

壓縮   
-dontshrink    不壓縮輸入的類文件   
-printusage {filename}   
-whyareyoukeeping {class_specification}       

優化   
-dontoptimize    不優化輸入的類文件   
-assumenosideeffects {class_specification}    優化時假設指定的方法,沒有任何反作用   
-allowaccessmodification    優化時容許訪問並修改有修飾符的類和類的成員   

混淆   
-dontobfuscate    不混淆輸入的類文件   
-printmapping {filename}   
-applymapping {filename}    重用映射增長混淆   
-obfuscationdictionary {filename}    使用給定文件中的關鍵字做爲要混淆方法的名稱   
-overloadaggressively    混淆時應用侵入式重載   
-useuniqueclassmembernames    肯定統一的混淆類的成員名稱來增長混淆   
-flattenpackagehierarchy {package_name}    從新包裝全部重命名的包並放在給定的單一包中   
-repackageclass {package_name}    從新包裝全部重命名的類文件中放在給定的單一包中   
-dontusemixedcaseclassnames    混淆時不會產生形形色色的類名   
-keepattributes {attribute_name,...}    保護給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and   

InnerClasses.   
-renamesourcefileattribute {string}    設置源文件中給定的字符串常量
複製代碼

實際代碼:

-ignorewarnings                     # 忽略警告,避免打包時某些警告出現 
-optimizationpasses 5               # 指定代碼的壓縮級別 
-dontusemixedcaseclassnames         # 是否使用大小寫混合 
-dontskipnonpubliclibraryclasses    # 是否混淆第三方jar 
-dontpreverify                      # 混淆時是否作預校驗 
-verbose                            # 混淆時是否記錄日誌 
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*        # 混淆時所採用的算法 

-libraryjars   libs/treecore.jar  

-dontwarn android.support.v4.**     #缺省proguard 會檢查每個引用是否正確,可是第三方庫裏面每每有些不會用到的類,沒有正確引用。若是不配置的話,系統就會報錯。 
-dontwarn android.os.**  
-keep class android.support.v4.** { *; }        # 保持哪些類不被混淆 
-keep class com.baidu.** { *; }    
-keep class vi.com.gdi.bgl.android.**{*;}  
-keep class android.os.**{*;}  

-keep interface android.support.v4.app.** { *; }    
-keep public class * extends android.support.v4.**    
-keep public class * extends android.app.Fragment  

-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 * extends android.support.v4.widget  
-keep public class * extends com.sqlcrypt.database  
-keep public class * extends com.sqlcrypt.database.sqlite  
-keep public class * extends com.treecore.**  
-keep public class * extends de.greenrobot.dao.**  


-keepclasseswithmembernames class * {       # 保持 native 方法不被混淆 
   native <methods>;  
}  

-keepclasseswithmembers class * {            # 保持自定義控件類不被混淆 
   public <init>(android.content.Context, android.util.AttributeSet);  
}  

-keepclasseswithmembers class * {            # 保持自定義控件類不被混淆 
   public <init>(android.content.Context, android.util.AttributeSet, int);  
}  

-keepclassmembers class * extends android.app.Activity { //保持類成員  
  public void *(android.view.View);  
}  

-keepclassmembers enum * {                  # 保持枚舉 enum 類不被混淆 
   public static **[] values();  
   public static ** valueOf(java.lang.String);  
}  

-keep class * implements android.os.Parcelable {    # 保持 Parcelable 不被混淆 
 public static final android.os.Parcelable$Creator *;  
}  

-keep class MyClass;                              # 保持本身定義的類不被混淆
複製代碼

**注意: ** 在Android中有一部分是不可以被混淆的,混淆了以後就會出現異常:

  • 四大組件因爲在Mainfest中註冊了,因此不能被混淆
  • jin調用的Java的接口方法
  • 系統接口方法
  • R文件的混淆可能會致使引用錯誤(若是有地方使用反射機制,R文件被混淆以後就會找不到資源)

防止反編譯

防止反編譯方法

1 . Proguard混淆不只僅能夠混淆代碼,還能反編譯工具失效或者奔潰

2 . 使用如今國內的APK加固方法,就我所知目前你想要在360市場和騰訊的市場上發佈應用,都要必需要使用他們對應的市場的加固方式:360加固和樂固加固

咱們經常使用的反編譯工具備哪些呢?

  • apkTool
  • baksmali
  • dex2.jar
  • JEB

咱們經過上面的兩種方法可讓這些反編譯工具反編譯出來不易閱讀甚至讓反編譯工具失效或者。

因爲前面已經提到過混淆的方法就不在贅述,使用國內的APK加固方法就更加的簡單了,到指定的官網下載工具加固便可,文檔描述很詳細。

防止模擬器

防止模擬器逆向分析

緣由:通常被處於逆向分析狀態是在Android模擬器中運行的,因此咱們只須要在代碼中判斷咱們當前的APK是否運行在模擬器中便可。

檢測是不是模擬器 通常有一下幾種方式:

  • 檢測模擬器上的幾個特殊文件
  • 檢測模擬器上的特殊號碼
  • 檢測設備IDS是否是「000000000000000」
  • 檢測是否還有傳感器、藍牙
  • 檢測手機上纔有的硬件信息
  • 檢測手機的運營商

下面我用代碼來演示一下:

檢查IDS

/**
    * 檢查IDS
    *
    * @param context
    * @return
    */
   public static boolean chechDeviceIDS(Context context) {
       @SuppressLint("ServiceCast")
       TelephonyManager telecomManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
       @SuppressLint("MissingPermission")
       String deviceId = telecomManager.getDeviceId();
       if (deviceId.equalsIgnoreCase(DEVICE_ID)) {
           Log.e(TAG, "chechDeviceIDS==" + DEVICE_ID);
           return true;
       }
       return false;
   }
複製代碼

檢查模擬器特有文件

/**
    * 檢查模擬器特有的文件
    *
    * @param context
    * @return
    */
   public static boolean chechDeviceFile(Context context) {
       for (int i = 0; i < DEVICE_FILE.length; i++) {
           String file_name = DEVICE_FILE[i];
           File qemu_file = new File(file_name);
           if (qemu_file.exists()) {
               Log.e(TAG, "chechDeviceFile==" + true);
               return true;
           }
       }
       return false;
   }
複製代碼

檢查模擬器特有號碼

/**
    * 檢查特有電話號碼
    *
    * @param context
    * @return
    */
   public static boolean chechDevicePhone(Context context) {
       @SuppressLint("ServiceCast")
       TelephonyManager telecomManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
       @SuppressLint("MissingPermission")
       String phoneNumber = telecomManager.getLine1Number();
       for (String phone : DEVICE_PHONE) {
           if (phone.equalsIgnoreCase(phoneNumber)) {
               Log.e(TAG, "chechDevicePhone==" + phoneNumber);
               return true;
           }
       }
       return false;
   }
複製代碼

檢查模擬器是否含有這些設備

/**
    * 檢查特是否含有設備
    *
    * @param context
    * @return
    */
   public static boolean chechDeviceBuild(Context context) {
       String board = Build.BOARD;
       String bootloader = Build.BOOTLOADER;
       String brand = Build.BRAND;
       String device = Build.DEVICE;
       String hardware = Build.HARDWARE;
       String model = Build.MODEL;
       String product = Build.PRODUCT;

       if (board.equalsIgnoreCase("unknown") || bootloader.equalsIgnoreCase("unknown")
               || brand.equalsIgnoreCase("generic") || model.equalsIgnoreCase("sdk")
               || product.equalsIgnoreCase("goldfish")) {
           Log.e(TAG, "chechDeviceBuild==" + "find emulatorBuild");
           return true;
       }
       return false;
   }
複製代碼

運行結果:

咱們能夠看到打印結果是都顯示是模擬器,提醒一點獲取這些信息的時候別忘記添加權限:

經過上面咱們能夠判斷是不是模擬器,那麼這時咱們就能夠經過判斷來殺死APP或者殺死APP所在的進程。

二次打包

防止二次打包

  • 什麼是二次打包?
  • 經過反編譯工具獲得smali代碼,而後再由smali代碼,重打包造成APK,最後從新簽名才能運行。

若是程序處於破解狀態,那麼咱們的APK確定是要運行在真機或者模擬器上,必需要從新簽名,那麼此時的簽名和原來的簽名必然不一致,咱們就能夠在程序的入口判斷咱們的簽名,來以此判斷二次打包。

代碼以下:

public class MainActivity extends AppCompatActivity {
   private static final String TAG = "MainActivity";

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       Log.e(TAG, "getSignature=="+getSignature("demo.lt.com.repacking"));
   }

   private int getSignature(String packageName) {
       PackageManager packageManager = this.getPackageManager();
       PackageInfo info = null;
       int sig = 0;
       try {
           info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
           Signature[] signatures = info.signatures;
           sig = signatures[0].hashCode();
       } catch (Exception e) {
           sig = 0;
           e.printStackTrace();
       }

       return sig;
   }
}
複製代碼

獲取到簽名hashCode是惟一值:

所以咱們只須要再增長代碼便可判斷二次打包:

public class MainActivity extends AppCompatActivity {
   private static final String TAG = "MainActivity";

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       Log.e(TAG, "getSignature==" + getSignature("demo.lt.com.repacking"));
       if (getSignature("demo.lt.com.repacking") != -567503403) {
           Log.e(TAG, "被從新打包了");
       }
   }

   private int getSignature(String packageName) {
       PackageManager packageManager = this.getPackageManager();
       PackageInfo info = null;
       int sig = 0;
       try {
           info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
           Signature[] signatures = info.signatures;
           sig = signatures[0].hashCode();
       } catch (Exception e) {
           sig = 0;
           e.printStackTrace();
       }

       return sig;
   }
}
複製代碼

這裏只是打印了個Log,實際代碼中能夠結束進程或者殺死APP。

一句話總結

以上就是今天的Android 防禦的知識點,並非要求你有多麼精通Android防禦,而是咱們做爲一個Android開發者必需要知道這些知識點,我相信對你確定是有用的。

原創不易,若是以爲寫得好,掃碼關注一下點個贊,是我最大的動力。

關注我,必定會有意想不到的東西等你:Android、Java、大數據等視頻資源、書籍等着你來拿。 天天專一分享Android、JAVA乾貨

備註:程序圈LT
相關文章
相關標籤/搜索