Apk反编译实战

Apk反编译实战

前言: 学习Android开发已经一年多了,最近有些不务正业,不好好学开发,而去学了一些点逆向知识。。但毕竟还是搞Android的,今天写一篇文章总结一些apk的反编译。(注:反编译不是让各位去对别人的应用破解搞重装,主要目的是为了促进学习,提升自我开发水平。)


一. 初识apk

即使不是Android开发者,也应该知道,所谓apk,其实就是AndroidPackage的缩写,俗称安装包。做Android都知道,做出一个app后,想给别人玩玩,肯定要把我们的代码,资源文件等东西打包成apk,然后再签名(后面会讲),就可以发给别人愉快地玩耍了。

  • 虽然apk文件的后缀名是apk,但其实是zip格式,是可以被当成一个压缩文件解压的,那我们来试试,尝试直接解压一个简单的apk:

  • 上图就是直接解压所得,可以看到有五个部分:

    1. META_INF文件夹:这里面储存的是关于apk的签名信息,在安装apk时,系统会校验apk的签名信息,判断程序完整性(所以如果直接解压apk修改文件,再重新打包的话,是肯定安装不上的)。关于文件夹里具体文件的作用,不是本文的重点,就不讨论了。详细介绍

    2. res文件夹: 做Android的肯定知道,这里面存储着apk用到的资源文件,但这样就想拿到里面的布局文件?那是肯定不行的,为了减小文件大小以及保证一定程度的安全,里面的所有xml文件都是二进制的,并不能直接拿到(不过拿个图片还是可以的)。

    3. AndroidManifest.xml:这东西大家肯定非常熟悉,是一个存了一大堆程序配置信息的清单文件,当然也是二进制的。

    4. class.dex(有时候不止一个dex文件): DEX文件(DalvikVM executes),顾名思义,是Android Dalvik虚拟机上的执行程序,也就是Dalvik字节码,程序的代码都在里面(反编译看源码的关键)。

    5. resources.arsc:编译后的二进制资源文件。


二. 反编译

大致了解了apk的构成,就可以开始反编译,拿到我们想要的东西了。

当然,工欲善其事,必先利其器。目前反编译最常用的是三大工具:

  • apktool:反编译apk的利器,还可重新打包。

  • dex2jar: 把dex文件反编译成jar文件,获取源码。

  • jd-gui:查看jar源码,当然用其他工具也能看。

    关于这些工具的下载安装,就不赘述了(上各自的官网下载就行了)。

    由于前两个工具要在命令行操作,所以把它们放在一起,然后把目录加到环境变量PATH里面,用起来方便一些。

开始实战

1. 既然要反编译,那肯定要先准备一个apk,本着尊重他人劳动成果的意愿,就自己写个小demo用来反编译吧。这个demo很简单,就两个页面:


第一个页面

第二个页面

2. 把apk文件放到反编译工具所在的文件夹(如果把apktool等工具加入PATH的话,就不用这一步了)

3. 用apktool反编译(在apk文件所在文件夹处打开powershell/cmd)
1
apktool.bat d demo.apk

即可在该处产生一个同名的demo文件夹,反编译的结果就在里面

此时res下的所有xml文件以及AndroidManifest都是可读的了,资源文件get。

  • smali文件夹:存放着源代码反编译出的smali文件

    Smali,Baksmali分别是指安卓系统里的Java虚拟机(Dalvik)所使用的一种.dex格式文件的汇编器,反汇编器。

    其语法是一种宽松式的Jasmin/dedexer语法,而且它实现了.dex格式所有功能(注解,调试信息,线路信息等)。(百度百科)

  • 通过文件名,都能大概看出这些smali文件来自哪些Java类了($表示内部类)

    • 用文本编辑器打开这些文件看看,应该还是能大致看出具体什么意思的(也可以自己去学学smali的语法),但是肯定没Java看着爽,所以我们进入下一步。
    4. 用dex2jar反编译查看Java源码
    • 首先,跟最开始一样解压apk压缩包,拿出其中的 dex 文件,放到dex2jar文件夹里(请下载最新版的dex2jar2.1)

      1
      .\d2j-dex2jar.bat classes.dex
    • 然后会在当前文件夹生成classes-dex2jar.jar,这就是源码了,用jd-gui打开,即刻查看源码,如果没有进行混淆的话,看上去大概这个样子

三.修改程序并二次打包

改点啥呢?当然,能修改的东西很多,比如我把apk里的静态图片资源给换掉,把默认启动的activity给换掉,或者分析smali源码并且修改,然后用apktool二次打包(后面讲)就行了。我这次决定改的是:写一个新的activity,并把其整合到原app里面(比如给原app加个开屏广告(不道德的行为,别这么做))。

  • 具体的实现方式是:

    1. 自己写一个新的FakeActivity.java,以及layout文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //包名要和原apk的包名一样,当然也可以生成smali之后直接修改smali里的包名
    package com.fenghaha.demo;
    import android.os.Bundle;
    import androidx.appcompat.app.AppCompatActivity;
    public class FakeActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fake);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FakeActivity">
    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:textSize="25sp"
    android:text="I'm a fake Activity!"/>
    </RelativeLayout>

2. 把Java文件编译成smali

这一步有两种方法:

  1. AS或者idea有个叫做java2smali的插件,装上后直接Bulid=>Compile to smali搞定,但我由于用了androidx库,这个插件似乎不能成功运行。遂选择第二种方法。

  2. 建立一个新项目,写好Java文件和layout文件,直接打包apk,然后再反编译(我反编译我自己),也能得到smali文件。如果Java文件里有内部类(比如按键监听器),会生成多个smali文件

  3. 直接手写smali(我不会,跳过)

    • 得到的smali如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    .class public Lcom/fenghaha/demo/FakeActivity;
    .super Landroidx/appcompat/app/AppCompatActivity;
    .source "FakeActivity.java"


    # direct methods
    .method public constructor <init>()V
    .locals 0

    .line 7
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

    return-void
    .end method


    # virtual methods
    .method protected onCreate(Landroid/os/Bundle;)V
    .locals 0

    .line 11
    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    const p1, 0x7f09001c

    .line 12
    invoke-virtual {p0, p1}, Lcom/fenghaha/demo/FakeActivity;->setContentView(I)V

    return-void
    .end method

3. 把得到的FakeActivity.smali以及activity_fake.xml这个layout文件整合到最开始反编译得到的demo文件夹里面去,并且修改代码,把默认启动的activity改为FakeActivity。

  1. 把FakeActivity.smali放到demo\smali\com\fenghaha\demo\ 目录下

  1. 把activity_fake.xml放到\demo\res\layout 目录下

  2. 把自己新加的activity加到AndroidManifest里并设为默认启动

  3. 为新加的activity添加资源id

    • layout资源id文件存在于两个地方 \demo\res\values\public.xml和\demo\smali\com\fenghaha\demo\R$layout.smali,亲测发现修改任意一个文件都可以
      1. public.xml
      2. R$layout.smali 添加方法类似
  4. 修改setContentView里的id,改为刚刚添加的。

  5. 重新打包

    • 利用apktool的打包功能重新打包

      1
      2
      .\apktool.bat b demo -o new.apk
      # b表示打包 demo指的是用于打包的文件夹 -o xxx\xxx.apk 表示输出apk的路径以及文件名 如果不指定-o参数的话 apk默认生成于demo\dist目录下

四. 重新签名

刚才重新打包生成的apk是无法安装的,因为只有签名过的apk才允许被安装

接下来讲一下签名未打包的apk

我用的是Java自带的签名工具jarsigner.exe 位于xxx\jdk\bin目录下(当然安装jdk的时候一般都设置了环境变量的,所以应该能直接调用)。

做Android的也肯定知道,生成一个签名的apk需要一个签名证书,xxx.jks,如果没有可以用AS生成一个

然后把jks文件拷贝一份到要签名的apk目录下

  • 用jarsigner签名apk(jdk1.7以上)

    1
    2
    3
    4
    5
    6
    #可以先看看apk是否被签名
    jarsigner -verify new.apk
    #签名
    jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore fhh.jks -storepass xxxxxxxxx -signedjar signed.apk new.apk fenghaha
    #参数含义
    #jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore [签名证书文件名] -storepass [签名证书密码] -signedjar [签名后生成apk的文件] [被签名的apk文件] [签名证书的别名]
  • 签名完成后就会在当前目录下生成一个sign.apk,安装到手机上:

    • 可以看到,新添加进去的activity已经成功变了默认的activity。

五. 总结

整个流程下来,虽然看上去很顺利,但实际操作上还是有很多问题的,比如想反编译的apk是被混淆过的,或者被加固过,又或者在native层做了签名校验,反编译的难度就会难很多。

但安全攻防,有加固那也有脱壳,也有.so库的反编译,不过作为一个做开发而不是做安全的,也没有必要研究得太深入),通过这次反编译实战,理解一下Android项目的结构,编译过程,发布流程以及一点点的安全防护方面的知识,感觉也足够了(赶紧回去乖乖学Android开发)。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×