当前位置: 首页 > 科技观察

Android混淆从入门到精通

时间:2023-03-12 20:50:09 科技观察

介绍作为一个Android开发者,如果你不想开源你的应用,你需要在应用发布之前对代码进行混淆,这样即使代码也很难阅读被反编译。虽然容易混淆概念,但很多初学者只是在网上搜索一些成型的混淆规则,然后粘贴到自己的项目中,并没有对混淆有深入的理解。这篇文章的目的是让一个初学者在阅读后能够在没有任何帮助的情况下独立编写适合自己代码的混淆规则。上面我们直接用AndroidStudio来说明如何混淆。AndroidStudio本身集成了Java语言的ProGuard作为压缩、优化和混淆工具。它非常易于与Gradle构建工具一起使用。你只需要在项目应用目录下的gradle文件中设置minifyEnabled为true即可。然后我们可以将混淆规则添加到proguard-rules.pro文件中。android{...buildTypes{release{minifyEnabledtrueproguardFilesgetDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'}}}以上示例代码表示release版本可以混淆。下面先简单介绍一下ProGuard的三个功能,并简单说明一下它们常用的命令。ProGuardShrinking:默认启用,减少应用程序的大小,移除不用的类和成员,优化动作执行后再次执行(因为优化后可能会再次暴露一些不用的类和成员)。-dontshrink关闭压缩优化(Optimization):默认启用,在字节码级别进行优化,使应用程序运行得更快。-dontoptimize关闭优化-optimizationpassesn表示proguard迭代优化代码的次数,Android一般为5次。Obfuscation:默认开启,增加反编译难度。班级和班级成员将被随机命名,除非他们受到keep的保护。-dontobfuscate关闭混淆混淆后默认会在项目目录app/build/outputs/mapping/release下生成一个mapping.txt文件。这就是混淆规则。我们可以根据这个文件把混淆后的代码推回源码,所以这个文件很重要,注意保护好。原则上混淆后的代码越乱越不规则越好,但是有些地方需要避免混淆,否则程序会跑错,所以下面我们要教大家的是,如何避免一些地方混淆你的代码从而防止错误。基本规则先看下面两个常用的命令。很多童鞋可能会对以下两个命令的区别感到困惑。-keepclasscn.hadcn.test.**-keepclasscn.hadcn.test.*一星表示只保留包下的类名,子包下的类名仍然会混淆;两颗星表示保留包和包含的子包下的类名;使用上面的方法保留类后,你会发现类名虽然没有混淆,但是里面具体的方法和变量名都变了。如果既要保留类名,又要让里面的内容不被混淆,我们需要以下方法-keepclasscn.hadcn.test.*{*;}在此基础上,我们还可以使用Java的基本规则为了保护特定的类不被混淆,例如,我们可以使用扩展和实现等Java规则。下面的例子防止所有继承自Activity的类被混淆-keeppublicclass*extendsandroid.app.Activity如果我们想让一个类中的内部类不被混淆,就需要使用$符号。下面的例子意味着在ScriptFragment的JavaScriptInterface内部类中保持所有public内容不被混淆。-keepclassmembersclasscc.ninty.chat.ui.fragment.ScriptFragment$JavaScriptInterface{public*;}此外,如果你不想让一个类中的所有内容不被混淆,而只想保护类下的特定内容,你可以使用;//匹配所有的构造函数;//匹配所有的域;//匹配所有的方法也可以在或<前面加上private,public,native等methods>进一步指定不会混淆的内容,比如-keepclasscn.hadcn.test.One{public;}表示One类下的所有public方法都不会混淆,当然你也可以添加参数,例如下面的意思就是使用JSONObject作为入参的构造函数就不会混淆了——keepclasscn.hadcn.test.One{public(org.json.JSONObject);}有时候你还是会想,我不需要保留类名,我只需要保留这个类下的具体方法不混淆即可,那就不能用keep了方法,keep方法会保留类名,但是需要使用keepclassmembers,所以不会保留类名,为了方便这些规则理解,官网给出下表防止被移除或者重命名为防止被重命名类和类成员-keep-keepnames只有类成员-keepclassmembers-keepclassmembernames如果有成员,保留类和类成员-keepclasseswithmembers-keepclasseswithmembernames删除是指在缩容时是否删除上述内容。掌握混淆规则很重要。理解之后,基本上所有的混淆规则文件你应该都能看懂了。再加上以下注意事项,注意1,jni方法不能混淆,因为这个方法需要和native方法保持一致;-keepclasseswithmembernamesclass*{#保持native方法不被混淆native;}2、反射使用的类不被混淆(否则反射可能有问题);3、AndroidMainfest中的类是不混淆的,所以Application的四大组件和子类以及Framework层下的所有类默认是不会混淆的。自定义View默认不会混淆;因此,无需添加网上发布的排除自定义View或AndroidStudio中四大组件混淆的规则;4、与服务端交互时,使用GSON,fastjson等框架在解析服务端数据时,写的JSON对象类不要混淆,否则无法将JSON解析成相应的对象;5、在使用第三方开源库或引用其他第三方SDK包时,如有特殊需求,还需要在混淆文件中添加相应的混淆规则;6、使用WebView的JS调用也需要保证写的接口方法不被混淆。混淆,否则会产生Android.os.BadParcelableException;-keepclass*implementsAndroid.os.Parcelable{#KeepParcelable不被混淆publicstaticfinalAndroid.os.Parcelable$Creator*;}8、使用enum类型时,需要注意避免以下两种方法混淆,因为的特殊性枚举类,下面两个方法会被反射调用,见规则二。-keepclassmembersenum*{publicstatic**[]values();publicstatic**valueOf(java.lang.String);}写在***发布一个应用除了设置minifyEnabled为true外,还应该设置zipAlignEnabled为true,像GooglePlay强制开发者上传的应用程序必须是zipAligned。ZipAlign可以将安装包中的资源以4字节对齐,可以减少应用程序在运行时的内存消耗。