首先我们要搞清楚这两个概念,反编译,顾名思义就是将已经编译好的apk还原成代码的过程,apk是一种压缩文件,但我们不能将反编译简单的理解为单纯的解压过程。
混淆的目的有两个:
(1)优化代码
(2)在一定程度上防止反编译,增加逆向难度保护代码安全。
下载最新版本即可,作用:用来查看xml文件,和资源文件等
作用:反编译java源码
作用最用查看jar文件
以上工具推荐下载最新版本,我把我用到的打包发出来,需要的可以自行下载 提取码:kmui
反编译可以分为两个部分,对资源文件的反编译和对代码的反编译。
这一步主要是为了反编译出 AndroidManifest.xml 文件以及其他的 xml文件 和 图片 等资源文件,apk既然是一种压缩格式,为什么不能直接解压呢,我们可以试着将apk的文件名直接改成zip然后解压,解压成功后的目录结构如下。(注意红色框出的classes.dex文件后面会用到)
我们发现已经解压出了xml文件,但是这种方式解压出来会造成乱码,打开AndroidManifest文件如下。
在path中配置apktool的环境变量,将需要反编译的apk文件拷贝到apktool目录下,命令行cd到apktool的目录(apktool.bat所在目录) 输入命令 apktool.bat d ***.apk
或者 apktool.bat d -f ***.apk filepath -f是将生成文件保存到指定目录。
这样就成功了,打开生成文件夹中的xml就可以正常查看了
生成的目录结构中我们并没有发现java源码在哪,其实smali中就是java源码,只不过没法直接转换为java文件进行查看。
接下来我们反编译java代码部分,首先将第一步我们直接解压apk生成的classes.dex文件(它就是java文件编译再通过dx工具打包而成的)拷贝到dex2jar-2.0下,即d2j-dex2jar.bat所在的目录,cd到这个目录下输入命令 dex2jar.bat classes.dex 运行完成后会生成一个classes-dex2jar.jar的文件。用jd-gui工具打开即可查看到java代码。
这时候我们惊喜的发现好多类名变量名都是abcdefg..难道大神都用abc写代码吗!?
我们接着往下看。
为了不让自己的代码这么容易的反编译,就有了代码混淆机制,既然阻止不了辛辛苦苦写出来的代码被反编译那就让你看不懂我的代码。Android中混淆使用到的是proguard。下面介绍在as中混淆代码的方法。
首先打开build.gradle文件
proguard-rules.pro文件用来规定当前的混淆规则:
具体介绍如下:
#混淆过滤规则
# -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 不混淆输入的类文件
# -obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法的名称
# -overloadaggressively 混淆时应用侵入式重载
# -useuniqueclassmembernames 确定统一的混淆类的成员名称来增加混淆
# -flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
# -repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
# -dontusemixedcaseclassnames 混淆时不会产生形形色色的类名
# -keepattributes {attribute_name,...} 保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
# -renamesourcefileattribute {string} 设置源文件中给定的字符串常量
# 不能混淆的地方
# 如果混淆的太严重的话,可能会造成程序不能正常使用,所以有些东西不能进行混淆(会被外部引用的地方),下面列举了几个不能混淆的地方:
# AndroidManifest中注册的Android组件;
# 在XML中使用的自定义View;
# Android生成的资源索引文件R.Java;
# 通过反射调用的类,方法等不能进行混淆;
# 通过Jni与so通信的Native的方法不能进行混淆;
# 通过WebView与JavaScript通信的方法不能进行混淆;
试一下
混淆后