Android反编译笔记
Android反编译笔记
APK是Android Package的缩写,即Android安装包(APK)。APK是类似Symbian Sis或Sisx的文件格式。通过将APK文件直接传到Android模拟器或Android手机中执行即可安装。APK文件和sis一样,把android sdk编译的工程打包成一个安装程序文件,格式为apk。
APK文件其实是zip格式,但后缀名被修改为apk,通过UnZip解压后,可以看到Dex文件,Dex是Dalvik VM executes的简称,即Android Dalvik执行程序,并非Java ME的字节码而是Dalvik字节码。
apk反编译就是通过使用apk反编译工具将apk文件中的源文件和资源反编译出来,得到的源文件和资源文件可以进行处理后再进行编译,以达到系统解剖分析,个性化定制,汉化等目的。
使用ApkTool及Dex2jar逆向Android的应用程序APK,
并用jd-gui查看java源代码。
ApkTool.jar
反编译使用的命令
apktool.jar d HelloWord.apk
反编译结束后会在同目录生成一个同名文件夹,里面有反编译后的所有信息。
反编译后我们可以看到,各种资源文件,向res里面的图片以及界面资源等。还可以看到AndroidManifest.xml里面的内容。
Dex2jar
要从APK文件获得JAR包需要使用专门的工具dex2jar。dex2jar 是一个用来将 Android 的 Dalvik Executable (.dex) format 文件转成 Java 类文件的工具。
用dex2jar反编译的到源码,查看源码结构。
Dex2jar是一款可以将calsses.dex反编译得到java的工具,但还不是成熟,得到的源码并不完全准确。
使用apktool反编译得到apk的classes.dex文件,然后取出里面的calsses.dex文件到Dex2jar目录下
使用命令
dex2jar.bat classes.dex
dex2jar会把classes.dex反编译为classes_dex2jar.jar文件,就在dex2jar的目录下,反编译成jar包
jd-gui
反编译完成之后,用jd-gui(一款查看源码的工具)打开这个jar包。这个java源码虽然不完整,但是大致逻辑是很清楚的。当然这个反编译是不可逆的,也就是说用这段源码想重新编译为原来apk的样子可能性很低,程序越复杂越低。这里可以看到变量名以及类名还很自然,如果加入混淆之后,变量名和类名都很难分清。
了解smali结构
使用Apktool反编译apk后,会在反编译目录下生成一个smali文件夹,里面存放着的就是所有反编译出的smali文件,这些文件会根据程序包的层次结构生成相应的目录,程序中所有的类都会在相应的目录下生成独立的smali文件。baksmali在反编译文件时,会为每个类单独生成了一个smali文件,内部类作为一个独立的类,它也拥有自己独立的smali文件,其文件名以“外部类名$内部类名”的形式定义。如HelloWorld\smali\com\example\helloworld目录下的R$layout.smali文件。
打开HelloWorld文件夹下的AndroidManifest.xml文件,其中有如下片段的代码:
<activity android:label="@string/app_name"
android:name="com.example.helloworld.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
其中,android.intent.category.LAUNCHER表示这个Activity可以通过LAUNCHER来启动,android.intent.action.MAIN表示MainActivity为helloworld程序的主Activity,我们一般分析代码,就从主Activity入手。helloworld的完整类名是:com.example.helloworld.MainActivity
分析程序的主Activity
使用记事本打开HelloWorld\smali\com\example\helloworld\MainActivity.smali文件。每个smali文件都由若干条语句组成,所有语句都遵循着同一套语法规范。
如前三行代码:
.class public Lcom/example/helloworld/MainActivity;
.super Landroid/app/Activity;
.source "MainActivity.java"
分别表示具有public访问权限的类com/example/helloworld/MainActivity,其父类是android/app/Activity,源文件名是MainActivity.java。
再比如:
.field private info:Ljava/lang/String;
//表示一个私有字段info, 它的类型为“Ljava/lang/String;”
一些常见的Smali语法如下:
.field private isFlag:z 定义变量
.method 方法
.parameter 方法参数
.prologue 方法开始
.line 12 此方法位于第12行
invoke-super 调用父函数
const/high16 v0, 0x7fo3 把0x7fo3赋值给v0
invoke-direct 调用函数
return-void 函数返回void
.end method 函数结束
new-instance 创建实例
iput-object 对象赋值
iget-object 调用对象
invoke-static 调用静态函数
Android代码混淆
Android代码混淆是Android平台应用开发者经常使用到的一种防止被反编译破解分析的方法。由于Android应用程序主要是由Java语言编写的,Java源代码编译成的中间字节码文件,非常容易被反编译成Java源码。为了防止这种现象出现,就出现了代码混淆技术。
混淆就是对待发布的程序代码进行重新组织和整理,经过混淆后的程序保持原有功能不变,但混淆后的代码很难被反编译,即使反编译成功,也很难看出原来程序的真正语义。
阅读反编译的Java代码方法
在学习代码混淆技术的过程中,我们通过一种阅读反编译Java代码的方法来验证代码混淆的效果。
使用开源的dex2jar结合jd-gui工具,可以轻松实现将Dalvik虚拟机(DVM)的字节码到Java源代码的转换。其中,dex2jar用来将DVM字节码转换成Java语言的字节码,从而生成jar文件。jd-gui负责将jar文件转换成Java源文件供阅读。
关于JVM字节码和DVM字节码的区别
不同于JVM基于堆栈,DVM是基于寄存器的。
JVM字节码由一个或多个.class文件组成,而DVM字节码只由一个.dex文件构成。
JVM字节码使用不同的操作码来进行对象比较和null类型比较,而DVM则使用同一操作码进行比较。
对Android代码进行混淆
在Eclipse中打开crack工程,在工程目录中我们发现了proguard-project.txt和project.properties两个文件。我们就通过配置这两个文件来进行代码混淆。
打开project.properties文件,取消下边一行的注释:
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
(${sdk.dir}变量为当前主机Android SDK目录路径)。这行语句的作用是指定使用Android SDK中附带的proguard工具对代码进行混淆。混淆的具体配置在proguard-project.txt文件中进行。这里我们使用默认的混淆处理。
混淆配置完成后,在Eclipse中导出新的crack.apk。在Eclipse的Package Explorer中选中工程的根节点,在右键菜单里找到Android Tools。选择“Export Unsigned Application Package”菜单项,并选择新crack.apk的保存路径,我们不妨覆盖掉原来的crack.apk。这样,新生成的crack.apk已经经过了代码混淆处理。
如果要特别指定程序中某个jar包不需要被混淆,在proguard-project.txt中需要到
-keep
关键字指令。
重新反编译阅读Java代码,对比混淆前后效果.
重新使用dex2jar和jd-gui工具查看反编译后的Java代码,方法同步骤一所述。可以看到新的Java程序代码。可以看到,经过代码混淆后,一些Java类名已经做了处理。对于代码量很大的程序来讲,这无疑大大增加了逆向分析的难度。
使用proguard不建议对某些特殊地方的Java元素进行混淆,除了:自定义的静态方法代码。