Android应用防护

Android应用防护


本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/Rozol/article/details/89262879


混淆

混淆主要作用是减少apk的大小.

代码混淆

参考资料: https://developer.android.com/studio/build/shrink-code.html

混淆会使编译时间变长, 所以只在release模式下开启即可.

android {
    buildTypes {
        release {
            minifyEnabled true // 打开混淆
            shrinkResources true // 打开资源压缩
			zipAlignEnabled true // 让资源4字节对齐, 以减少内存消耗
            buildConfigField "boolean", "LOG_DEBUG", "false" // 不显示log
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

proguard-rules.pro添加规则

-keep class me.luzhuo.idaso.MainActivity {*;}

混淆之后(ProGuard)会生成以后文件:

  • dump.txt
    • 说明 APK 中所有类文件的内部结构。
  • mapping.txt
    • 提供原始与混淆过的类、方法和字段名称之间的转换。(可反推回为原本的代码, 每次发布均需保留一个)
  • seeds.txt
    • 列出未进行混淆的类和成员。
  • usage.txt
    • 列出从 APK 移除的代码。
  • 这些文件保存在 <module-name>/build/outputs/mapping/release/
混淆规则
  1. *** 的区别

    -keep class me.luzhuo.idaso.*  # 只保持 当前包 下的
    -keep class me.luzhuo.idaso.**  ## 保持 当前包 及其 所有子包 下的
    
  2. 保持 整个类 不被混淆

    -keep class me.luzhuo.idaso.MainActivity {*;}
    
  3. 保持 接口的实现 不被混淆

    -keep class * implements me.luzhuo.JavaScriptInterface {*;}
    
  4. 保持 整个包所有类 不被混淆

    -keep class me.luzhuo.idaso.** {*;}
    
  5. 保持 继承类 不被混淆

    -keep public class * extends android.app.Activity  # 保持所有继承 android.app.Activity 的类
    
  6. 保持 内部类 不被混淆

    -keepclassmembers class me.luzhuo.MainFragment$JavaScriptInterface {
       public *;
    }  # 保持MainFragment的内部类JavaScriptInterface所有public内容
    
    -keepclassmembers class me.luzhuo.MainFragment$* {
       *;
    }  # 保持MainFragment的所有内部类
    
  7. 保持某类下 指定内容

    -keep class me.luzhuo.MainActivity {
        <init>;     // 所有构造器
    	<fields>;   // 所有域
    	<methods>;  // 所有方法
    	public <init>(int, int); // 指定构造器
    }
    
  8. 保持某些指定 类型内容

    -keepclassmembers class me.luzhuo.MainActivity {
    	// public / private / protected
    	public <methods>;  // 所有public方法
    }
    
    -keepclassmembers class me.luzhuo.MainActivity {
    	public void show(java.lang.String) // 指定方法
    }
    
  9. 保持某类指定 参数内容

    -keep class me.luzhuo.MainActivity {
       public <init>(org.json.JSONObject);
    }
    
  10. 各种保持(keep)的区别

保持内容 保持代码和名字 保持名字
类名 + 类成员 -keep -keepnames
类成员 -keepclassmembers -keepclassmembernames
若有某成员, 则类名+类成员 -keepclasseswithmembers -keepclasseswithmembernames

举个栗子:

# 保持 native 方法不被混淆(jni要求类名和方法名均不能被混淆)
-keepclasseswithmembernames class * {
    native <methods>;
}
完整混淆配置清单
# 迭代优化次数
-optimizationpasses 5
# 混淆算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 不使用大小写混合, 全为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 不做预校验, 快混淆速度
-dontpreverify
-verbose
-renamesourcefileattribute SourceFile
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
-dontnote android.support.**
-dontwarn android.support.**

# 保持Annotation
-keepattributes *Annotation*,InnerClasses
# 保持 Annotation、内部类、泛型、匿名类
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
# 重命名异常时记录的文件名称
# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# 保持R的资源
-keep class **.R$* {*;}
# 保留四大组件、Application等这些类不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
# 保持onClick方法
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}
# 保持回调函数不被混淆
-keepclassmembers class * {
    void *(**On*Event);
    void *(**On*Listener);
}
# 保持native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
# 保持枚不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
# 保持Parcelable序列化不被混淆
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}
# 保持Serializable序列化不被混淆
-keepclassmembers class * 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();
}
# 保持Keep注解不被混淆
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmembers class * {
    @android.support.annotation.Keep *;
}

# 删除android.util.Log的所有输出日志
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** d(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
}

# ------ 第三方库要求的不被混淆 ------

资源混淆

开源库地址: https://github.com/shwenzhang/AndResGuard

AndResGuard主要帮你缩小apk大小, 例如将res/drawable/wechat变为r/d/a

在Project的build.gradle里添加以下配置:

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.15'
    }
}

在app下创建and_res_guard.gradle文件, 并copy以下内容做修改:

apply plugin: 'AndResGuard'

andResGuard {
    // mappingFile = file("./resource_mapping.txt")
    mappingFile = null
    use7zip = true
    useSign = true
    // 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字
    keepRoot = false
    // 指定保持不被混淆的资源列表, 其他见 https://github.com/shwenzhang/AndResGuard/blob/master/doc/white_list.md
    whiteList = [
            // for your icon
            "R.drawable.icon",
            // for fabric
            "R.string.com.crashlytics.*",
            // for google-services
            "R.string.google_app_id",
            "R.string.gcm_defaultSenderId",
            "R.string.default_web_client_id",
            "R.string.ga_trackingId",
            "R.string.firebase_database_url",
            "R.string.google_api_key",
            "R.string.google_crash_reporting_api_key"
    ]
    compressFilePattern = [
            "*.png",
            "*.jpg",
            "*.jpeg",
            "*.gif",
    ]
    sevenzip {
        artifact = 'com.tencent.mm:SevenZip:1.2.15'
        //path = "/usr/local/bin/7za"
    }

    /**
     * 可选: 如果不设置则会默认覆盖assemble输出的apk
     **/
    // finalApkBackupPath = "${project.rootDir}/final.apk"

    /**
     * 可选: 指定v1签名时生成jar文件的摘要算法
     * 默认值为“SHA-1”
     **/
    // digestalg = "SHA-256"
}

在App下的build.gradle里添加

apply from: 'and_res_guard.gradle'

生成包: 打开右边Gradle, 需要生成Release版, 双击resguardRelease即可, 会在/build/output/apk/release/AndResGuard_{apk_name}/生成相应的应用.
在这里插入图片描述

注:

  1. 需要配置签名才能正常打包, 这点很让人觉得讨厌.
  2. 若配置了代码混淆, 打包发布后便是 Code + Res 都混淆.

生成文件如下:
在这里插入图片描述

File Description
resources.arsc 修改后的resource.arsc文件
resource_mapping_output.txt 资源混淆的mapping文件
packagename_unsigned.apk 未签名apk
packagename_signed.apk sign模式, 已签名apk
packagename_signed_7zip.apk 7z重打包, 已签名apk
packagename_signed_aligned.apk sign模式, 对齐的, 已签名apk(可发布)
packagename_signed_7zip_aligned.apk 7z重打包, 对齐的, 已签名apk(可发布)

配置签名信息:
在app的build.gradle里配置

android {
    signingConfigs {
        myConfig {
            keyAlias 'key0'
            keyPassword '***'
            storeFile file('C:\\apk\\case.key.jks')
            storePassword '***'
        }
    }
    buildTypes {
        release {
			// ...
            signingConfig signingConfigs.myConfig // 坑爹的AndRedGuard必须要签名(若不用则不需), 否则编译失败
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

签名保护

众所周知, 每个应用必须有一个唯一的签名, 没被签名的应用将不允许被安装.

为了防止被二次打包, 我们需要做个签名验证, 签名不对就退出应用.

1.编写获取应用的指纹

public class SignatureVerifyUtils {
   
    // 正确的签名指纹的MD5
    private static final String APP_SIGN_MD5 = "2A:55:B2:A8:81:95:5C:6F:60:2F:AD:D8:5D:BE:49:C7";

    /**
     * 获取应用签名
     */
    public static String getSignatureMD5(Context context) {
   
        try {
   
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);

            byte[] cert = info.signatures[0].toByteArray();

            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] publicKey = md.digest(cert);
            StringBuilder hexString = new StringBuilder();
            for (int i = 0; i < publicKey.length; i++) {
   
                String appendString = Integer.toHexString(0xFF & publicKey[i]).toUpperCase(Locale.US);
                if (appendString.length() == 1)
                    hexString.append("0");
                hexString.append(appendString);
                hexString.append(":");
            }
            hexString.deleteCharAt(hexString.length() - 1);
            return hexString.toString();
        } catch (PackageManager.NameNotFoundException e) {
   
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
   
            e.printStackTrace();
        }
        return "";
    }

    // 对比签名
    public static boolean isOwnApp(Context context){
   
        return APP_SIGN_MD5.equals(getSignatureMD5(context));
    }

}

2.启动应用时进行指纹比较, 指纹不对就停止运行

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // --- 签名验证 ---
        // Java方式
        if (!SignatureVerifyUtils.isOwnApp(getApplicationContext())) {
            Process.killProcess(Process.myPid());
        }

        // JNI方式(略)
    }
}

jni的方式主要是native层调用getSignatureMD5()获取指纹, 并且在native层完成比较, 如果对不上则exit(0);退出.

获取签名文件的指纹

以上应用能获取到指纹, 那么我们如果从应用外获取签名的指纹呢.

1.通过命名方式

C:\Users\LZLuz>keytool -list -v -keystore C:\apk\case.key.jks
输入密钥库口令:

2.AndroidStudio自动的Gralde工具库, 运行signingReport.
在这里插入图片描述

3.使用jadx-gui工具打开apk包

4.把apk包下META-INF文件夹里的CERT.RSA拷贝出来, 执行以下命令查看

C:\Users\LZLuz>keytool -printcert -file C:\apk\KEY0.RSA

反调试检测

主要是检测/proc/[mypid]/status里的TracerPid字段的值是否为0, 不为0说明被其他进程调试, 则结束应用.

当程序运行以下代码时: 程序加载.so文件时, 会执行JNI_OnLoad函数; 卸载.so文件时, 会执行JNI_OnUnload函数.

static {
    System.loadLibrary("native-lib");
}

所以, 我们要在JNI_OnLoad函数里去创建线程, 在子线程里去判断TracerPid字段的值, 代码如下:

#include <
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AndResGuard 是一款 Android资源混淆工具,资源混淆组件不涉及编译过程,只需输入一个apk(无论签名与否,debug版,release版均可,在处理过程中会直接将原签名删除),可得到一个实现资源混淆后的apk(若在配置文件中输入签名信息,可自动重签名并对齐,得到可直接发布的apk)以及对应资源ID的mapping文件。同时可在配置文件中指定白名单,压缩文件(支持*,?通配符),支持自动签名,保持旧mapping,7z重打包,对齐等功能。 本工具支持 Linux、Window 跨平台使用,但测试表示若使用7z压缩,Linux 下的压缩率更高。示例代码:apply plugin: 'AndResGuard' buildscript {     dependencies {         classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.1.9'     } } andResGuard {     mappingFile = null     use7zip = true     useSign = true     keepRoot = false     whiteList = [         //for your icon         "R.drawable.icon",         //for fabric         "R.string.com.crashlytics.*",         //for umeng update         "R.string.umeng*",         "R.string.UM*",         "R.string.tb_*",         "R.layout.umeng*",         "R.layout.tb_*",         "R.drawable.umeng*",         "R.drawable.tb_*",         "R.anim.umeng*",         "R.color.umeng*",         "R.color.tb_*",         "R.style.*UM*",         "R.style.umeng*",         "R.id.umeng*"         //umeng share for sina         "R.drawable.sina*"     ]     compressFilePattern = [         "*.png",         "*.jpg",         "*.jpeg",         "*.gif",         "resources.arsc"     ]      sevenzip {          artifact = 'com.tencent.mm:SevenZip:1.1.9'          //path = "/usr/local/bin/7za"     } }简单用法:java -jar andresguard.jar input.apk若想指定配置文件或输出目录:java -jar andresguard.jar input.apk -config yourconfig.xml -out output_directory若想指定签名信息或mapping信息:java -jar andresguard.jar input.apk -config yourconfig.xml     -out output_directory -signature signature_file_path storepass_value     keypass_value storealias_value -mapping mapping_file_path若想指定7zip或zipalign的路径(若已设置环境变量,这两项不需要单独设置):java -jar andresguard.jar input.apk  -7zip /shwenzhang/tool/7za  -zipalign /shwenzhang/sdk/tools/zipalign若想用7zip重打包安装包,同时也可指定output路径,指定7zip或zipalign的路径(此模式其他参数都不支持):java -jar andresguard.jar -repackage input.apk -out output_directory  -7zip /shwenzhang/tool/7za  -zipalign /shwenzhang/sdk/tools/zipalign 标签:AndResGuard
Android应用安全测试与防护是一种对Android手机应用程序进行安全性评估和保护的过程。随着Android手机应用程序的快速增长,安全问题也逐渐凸显出来。因此,进行安全测试和防护是至关重要的。 在Android应用安全测试中,首先需要进行静态代码分析。通过对应用程序的源代码进行审查,可以发现潜在的安全漏洞和弱点。例如,是否存在恶意代码、是否使用了不安全的API等。 其次,进行动态测试也是必需的。通过对应用程序进行模拟攻击,可以确定其在实际环境中的防护能力。例如,测试应用程序是否容易受到信息泄露、数据篡改或者拒绝服务等攻击。 另外,还需要对应用程序的权限进行仔细审查。应用程序所申请的权限可能与其功能需求不匹配,这可能会导致潜在的安全问题。因此,应该对权限进行逐一审核,并确保其必要性和合理性。 在防护方面,可以采取一些措施来保护Android应用程序。首先是加密相关信息,例如使用安全的传输协议和加密算法来保护数据的传输和存储。同时,还可以使用身份验证和授权机制来管理用户的访问权限。 此外,及时更新应用程序也是防护的重要步骤。随着漏洞的不断修复和安全性的提升,及时更新应用程序可以及早消除潜在的安全风险。同时,用户应该选择可信赖的应用商店下载应用程序,以减少恶意软件的风险。 总而言之,Android应用安全测试与防护对于保护用户隐私和数据安全至关重要。通过静态代码分析、动态测试和权限审核,可以发现并修复潜在的安全漏洞。而加密、身份验证和及时更新等措施则可以提高应用程序的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值