Android安全防护

重打包

校验签名,签名的hashCode是唯一值

public static 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;
   }

模拟器

监测设备是否是模拟器

  • 检测模拟器上的几个特殊文件
  • 检测模拟器上的特殊号码
  • 检测设备IDS是不是“000000000000000”
  • 检测是否还有传感器、蓝牙
  • 检测手机上才有的硬件信息
  • 检测手机的运营商
/**
    * 检查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;
   }

反编译

Proguard混淆

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}    #使用给定文件中的关键字作为要混淆方法的名称  
#指定外部模糊字典
-obfuscationdictionary dictionary.txt
#指定class模糊字典
#-classobfuscationdictionary filename
#指定package模糊字典
#-packageobfuscationdictionary 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;                              # 保持自己定义的类不被混淆

注意

不能混淆的情况

  • 四大组件由于在Mainfest中注册了,所以不能被混淆
  • jin调用的Java的接口方法
  • 系统接口方法
  • R文件的混淆可能会导致引用错误(如果有地方使用反射机制,R文件被混淆之后就会找不到资源)
  • 第三方库指定不能混淆的类

加固