Android反编译简单实战

Android反编译简单实战

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

0. 初识apk

即使不是Android开发者,也应该知道,所谓apk,其实就是AndroidPackage的缩写,俗称安装包。做Android都知道,做出一个app后,想给别人玩玩,肯定要把我们的代码,资源文件等东西打包成apk,然后再签名(后面会讲),就可以发给别人愉快地玩耍了。
  • 虽然apk文件的后缀名是apk,但其实是zip格式,是可以被当成一个压缩文件解压的,那我们来试试,尝试直接解压一个简单的apk:
直接解压一个apk



  • 上图就是直接解压所得,可以看到有五个部分:
  • META_INF文件夹:这里面储存的是关于apk的签名信息,在安装apk时,系统会校验apk的签名信息,判断程序完整性(所以如果直接解压apk修改文件,再重新打包的话,是肯定安装不上的)。关于文件夹里具体文件的作用,不是本文的重点,就不讨论了。详细介绍
  • res文件夹: 做Android的肯定知道,这里面存储着apk用到的资源文件,但这样就想拿到里面的布局文件?那是肯定不行的,为了减小文件大小以及保证一定程度的安全,里面的所有xml文件都是二进制的,并不能直接拿到(不过拿个图片还是可以的)。
  • AndroidManifest.xml:这东西大家肯定非常熟悉,是一个存了一大堆程序配置信息的清单文件,当然也是二进制的。
  • class.dex(有时候不止一个dex文件): DEX文件(DalvikVM executes),顾名思义,是Android Dalvik虚拟机上的执行程序,也就是Dalvik字节码,程序的代码都在里面(反编译看源码的关键)。
  • resources.arsc:编译后的二进制资源文件。

1. 反编译

大致了解了apk的构成,就可以开始反编译,拿到我们想要的东西了。
当然,工欲善其事,必先利其器。目前反编译最常用的是三大工具:
  • apktool:反编译apk的利器,还可重新打包。
  • dex2jar: 把dex文件反编译成jar文件,获取源码。
  • jd-gui:查看jar源码,当然用其他工具也能看。
关于这些工具的下载安装,就不赘述了(上各自的官网下载就行了)。
由于前两个工具要在命令行操作,所以把它们放在一起,然后把目录加到环境变量PATH里 面,用起来方便一些。

开始实战

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



第二个页面


  • 把apk文件放到反编译工具所在的文件夹
工具和apk


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

    • apktool.bat d demo.apk
    • 即可在该处产生一个同名的demo文件夹,反编译的结果就在里面
反编译所得


  • 此时res下的所有xml文件以及AndroidManifest都是可读的了,资源文件get。
    • smali文件夹:里面存放着源代码反编译出的smali文件
Smali,Baksmali分别是指安卓系统里的Java虚拟机(Dalvik)所使用的一种.dex格式文件的汇编器,反汇编器。
其语法是一种宽松式的Jasmin/dedexer语法,而且它实现了.dex格式所有功能(注解,调试信息,线路信息等)。(百度百科)
smali文件夹


    • 通过文件名,都能大概看出这些smali文件来自哪些Java类了($表示内部类)
    • 用文本编辑器打开这些文件看看,应该还是能大致看出具体什么意思的(也可以自己去学学smali的语法),但是肯定没Java看着爽,所以我们进入下一步。
用dex2jar反编译查看Java源码
    • 首先,跟最开始一样解压apk压缩包,拿出其中的 dex 文件,放到dex2jar文件夹里(请下载最新版的dex2jar2.1)
.\d2j-dex2jar.bat classes.dex
  • 然后会在当前文件夹生成classes-dex2jar.jar,这就是源码了,用jd-gui打开,即刻查看源码,如果没有进行混淆的话,看上去大概这个样子
java源码



2. 修改程序并二次打包

改点啥呢?当然,能修改的东西很多,比如我把apk里的静态图片资源给换掉,把默认启动的activity给换掉,或者分析smali源码并且修改,然后用apktool二次打包(后面讲)就行了。我这次决定改的是:写一个新的activity,并把其整合到原app里面(比如给原app加个开屏广告(不道德的行为,别这么做))。
  • 具体的实现方式是:自己写一个新的FakeActivity.java,以及layout文件
 //包名要和原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); 
   }
}
  • xml
<?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>
  • 把Java文件编译成smali
    • 这一步有3种方法:

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

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

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

  • 得到的smali如下
.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

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


  1. 把FakeActivity.smali放到demo\smali\com\fenghaha\demo\ 目录下
添加smali文件


  • 把activity_fake.xml放到\demo\res\layout 目录下
    把自己新加的activity加到AndroidManifest里并设为默认启动
改为默认启动


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

      • layout资源id文件存在于两个地方 \demo\res\values\public.xml和\demo\smali\com\fenghaha\demo\R$layout.smali,亲测发现修改任意一个文件都可以
public.xml


R$layout.smali 添加方法类似


3. 修改setContentView里的id,改为刚刚添加的。

修改参数


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

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

3. 重新签名

刚才重新打包生成的apk是无法安装的,因为它还没有签名
接下来讲一下签名apk
我用的是Java自带的签名工具jarsigner.exe 位于xxx\jdk\bin目录下(当然安装jdk的时候一般都设置了环境变量的,所以应该能直接调用)。
做Android的也肯定知道,生成一个签名的apk需要一个签名证书,xxx.jks,如果没有可以 用AS生成一个
然后把jks文件拷贝一份到要签名的apk目录下
  • 用jarsigner签名apk(jdk1.7以上)
 #可以先看看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。

4. 总结

整个流程下来,虽然看上去很顺利,但实际操作上还是有很多问题的,比如想反编译的apk是被混淆过的,或者被加固过,又或者在native层做了签名校验,反编译的难度就会难很多。
但安全攻防,有加固那也有脱壳,也有.so库的反编译,不过作为一个做开发而不是做安全的,也没有必要研究得太深入),通过这次反编译实战,理解一下Android项目的结构,编译过程,发布流程以及一点点的安全防护方面的知识,感觉也足够了(赶紧回去乖乖学Android)。
编辑于 2018-11-30

文章被以下专栏收录