Android开发apk反编译和二次打包教程

作为Android开发者,工作中少不了要反编译别人的apk,当然主要目的还是为了学习到更多,取彼之长,补己之短。今天就来总结一下Android反编译和二次打包的一些知识。首先声明本文的目的是为了通过例子讲解反编译和二次打包的原理和方法,继而作为后续讲解防止二次打包和App安全的依据,并不是鼓励大家去重新打包别人的App,盗取他人劳动成果。

本文首先介绍几种Android反编译工具的使用,然后实现在不需要知道源代码的情况下,仅通过修改反编译得到的smali文件实现修改apk逻辑功能的目的。

Android中常用的反编译工具有三个:dex2jar、jd-gui和apktool,这三个工具的作用如下:

dex2jar:将apk中的classes.dex文件转换成jar文件。

jd-gui:查看由dex2jar转换成的jar文件,以界面的形式展示反编译出来的Java源代码。

apktool:反编译生成smali字节码文件,提取apk中的资源文件。

为了尽可能的把问题讲清楚,我们来实现一个很简单的例子。首先创建一个工程DecompileDemo,在MainActivity中定义一个布局,其中包含一个Button,点击会打印一段日志。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  private static final String TAG = "MainActivity";
  private Button btn;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
    Log.d(TAG,"Button is clicked");
  }
}

将这个工程编译生成的apk解压,取出其中的classes.dex放在dex2jar工具的目录下,然后执行命令

会在当前目录下生成class-dex2jar.jar文件

然后打开jd-gui,将class-dex2jar.jar文件拖进去,就可以看到反编译出来的源代码。

可以看到反编译的代码和原本的代码差别不大,主要差别是原来的资源引用全都变成了数字。

下面我们来修改这个apk的内容。

首先我们将apk拷贝到apktool工具目录下,执行命令apktool  d  app-release.apk。

生成的目录中包含smali文件夹

然后找到我们的主要的类MainActivity.smali,文件内容如下:

.class public Lcom/viclee/decompiledemo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"

# interfaces
.implements Landroid/view/View$OnClickListener;


# static fields
.field private static final TAG:Ljava/lang/String; = "MainActivity"

# instance fields
.field private btn:Landroid/widget/Button;

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

  .prologue
  .line 9
  invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

  return-void
.end method

# virtual methods
.method public onClick(Landroid/view/View;)V
  .locals 2
  .param p1, "v"  # Landroid/view/View;

  .prologue
  .line 23
  const-string v0, "MainActivity"

  const-string v1, "Button is clicked"

  invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

  .line 24
  return-void
.end method

.method protected onCreate(Landroid/os/Bundle;)V
  .locals 1
  .param p1, "savedInstanceState"  # Landroid/os/Bundle;

  .prologue
  .line 14
  invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

  .line 15
  const v0, 0x7f040019

  invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V

  .line 17
  const v0, 0x7f0c0050

  invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->findViewById(I)Landroid/view/View;

  move-result-object v0

  check-cast v0, Landroid/widget/Button;

  iput-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;

  .line 18
  iget-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;

  invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

  .line 19
  return-void
.end method

其中36-40行是打印日志的位置,文件内容很清晰,每个区域的意义如下:

.class  类名

.super 父类名

.source  文件名

.implements  这个类实现的接口

.field  成员变量

.method 方法

然后新建一个工程,在这个工程中实现想要替换的代码,我们这里是希望将原始工程中打印日志的地方替换为弹出一个Toast。

public class MainActivity extends AppCompatActivity{
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    showToast();
  }

  public void showToast() {
    Toast.makeText(this,"我是反编译后进行的修改。",Toast.LENGTH_LONG).show();
  }
}

然后像前面一样执行apktool命令,生成的smali文件内容如下:

.class public Lcom/viclee/decompiledemo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"

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

  .prologue
  .line 7
  invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

  return-void
.end method

# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
  .locals 1
  .param p1, "savedInstanceState"  # Landroid/os/Bundle;

  .prologue
  .line 10
  invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

  .line 11
  const v0, 0x7f040019

  invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V

  .line 13
  invoke-virtual {p0}, Lcom/viclee/decompiledemo/MainActivity;->showToast()V

  .line 14
  return-void
.end method

.method public showToast()V
  .locals 2

  .prologue
  .line 17
  const-string v0, "\u6211\u662f\u53cd\u7f16\u8bd1\u540e\u8fdb\u884c\u7684\u4fee\u6539\u3002"

  const/4 v1, 0x1

  invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

  move-result-object v0

  invoke-virtual {v0}, Landroid/widget/Toast;->show()V

  .line 18
  return-void
.end method

上面代码中,33、39-56行就是弹出Toast的代码部分。将上面整个showToast方法拷贝到原始工程的smali文件中,这里要特别注意修改行号,这个行号表示的是代码在原始Java文件中的行号,需要参考两个smali文件的行号来修改。我认为只要保证方法内的行号不乱序,并且方法之间的行号不冲突就可以。然后,需要将原始工程中打印日志的代码替换为显示Toast的代码,也就是将原始smali文件中36-40行修改为新建工程中33、39-56行的内容。修改后的内容如下,主要关注下面内容中36行、75-91行与原始smali文件的差异。

.class public Lcom/viclee/decompiledemo/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"

# interfaces
.implements Landroid/view/View$OnClickListener;

# static fields
.field private static final TAG:Ljava/lang/String; = "MainActivity"

# instance fields
.field private btn:Landroid/widget/Button;

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

  .prologue
  .line 9
  invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

  return-void
.end method

# virtual methods
.method public onClick(Landroid/view/View;)V
  .locals 2
  .param p1, "v"  # Landroid/view/View;

  .prologue
  .line 23
  invoke-virtual {p0}, Lcom/viclee/decompiledemo/MainActivity;->showToast()V

  .line 24
  return-void
.end method

.method protected onCreate(Landroid/os/Bundle;)V
  .locals 1
  .param p1, "savedInstanceState"  # Landroid/os/Bundle;

  .prologue
  .line 14
  invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

  .line 15
  const v0, 0x7f040019

  invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V

  .line 17
  const v0, 0x7f0c0050

  invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->findViewById(I)Landroid/view/View;

  move-result-object v0

  check-cast v0, Landroid/widget/Button;

  iput-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;

  .line 18
  iget-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;

  invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

  .line 19
  return-void
.end method

.method public showToast()V
  .locals 2

  .prologue
  .line 27
  const-string v0, "\u6211\u662f\u53cd\u7f16\u8bd1\u540e\u8fdb\u884c\u7684\u4fee\u6539\u3002"

  const/4 v1, 0x1

  invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

  move-result-object v0

  invoke-virtual {v0}, Landroid/widget/Toast;->show()V

  .line 28
  return-void

然后我们需要将修改后的文件目录重新打包,执行命令 apktool   b  app-release,就会在app-releae目录下生成两个文件夹:build 文件夹里面是一些中间文件(classes.dex等内容),dist 文件夹里面存放着重新打包出来的apk文件。

最后还要记得对生成的apk进行签名,否则安装时会报错。执行下面的命令行:

jarsigner -verbose -keystore viclee.keystore -signedjar app-release-signed.apk app-release.apk viclee.keystore

-verbose 输出签名详细信息 
-keystore 指定密钥对的存储路径 
-signedjar 后面三个参数分别是签名后的apk、未签名的apk和密钥对的别名

安装签名后的apk,点击按钮,确实弹出了Toast,内容和我们所设置的一致,说明我们的修改成功了。

我们注意到,修改smali文件的时候并不是直接在文件上进行修改,毕竟smali文件的可读性差,直接修改是十分困难的。我们的解决办法是新建一个工程将需要增加的代码实现,最好抽成一个单独的方法(方便替换),然后将新工程打包产生的apk反编译,得到对应的smali文件,再用其中的内容对原始smali文件进行替换。这样的修改方式降低了修改的难度也减小了犯错误的风险。

另外,apk反编译后也可以修改资源,将反编译出来的资源文件修改一通,然后按照之前的方法,重新打包、签名、安装。下面两个页面是修改之前和修改之后的对比图。

到这里,本文的全部内容就讲解完了,欢迎大家评论交流~

(0)

相关推荐

  • Android笔记之:App自动化之使用Ant编译项目多渠道打包的使用详解

    随着工程越来越复杂,项目越来越多,以及平台的迁移(我最近就迁了2回),还有各大市场的发布,自动化编译android项目的需求越来越强烈,后面如果考虑做持续集成的话,会更加强烈.    经过不断的尝试,在ubuntu环境下,以花界为例,我将一步一步演示如何使用命令行,使用ant编译android项目,打包多渠道APK.    要点:    (1). 编译android的命令使用    (2). ant基本应用    (3). 多项目如何编译(包含android library)    (4). 如

  • 在Android打包中区分测试和正式环境浅析

    前言 本文主要介绍了关于Android打包中区分测试和正式环境的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: APK打包正式环境和测试环境注意 最近在项目打包中,遇到一个问题,每次打包都要区分正式发布包和测试版本的包,主要是修改一下配置 public static final boolean EXTERNAL_RELEASE = true; 当变量为true的时候,就是正式包,反之就是测试包.每次上线的时候我就提醒自己要小心,千万不能出错了,然而终于湿脚了,一个渠道打

  • 怎么发布打包并发布自己的Android应用(APP)

    第一步,在Eclipse中选择需要打包的项目,然后右键--选择Export,会弹出一个打包的提示框,如下图所示. 按Next之后,会继续出现一个提示框,这里你可以选择自己需要打包的项目(默认是刚才选中的)如下图: 按Next之后,会弹出一个关于"Keystore"的提示,选择"Create new Keystore",并浏览.选择签名文件要保存的路径, (关于Keystore的原理.作用等这里暂时不细说,请大家发布前务必保存好该签名文件,非常重要的.) 如下图所示:

  • Android aapt自动打包工具详细介绍

    Android aapt自动打包工具 概念 在Android.mk中有LOCAL_AAPT_FLAGS配置项,在gradle中也有aaptOptions,那么aapt到底是干什么的呢? aapt即Android Asset Packaging Tool(Android 打包工具),在SDK的build-tools目录下.我们可以查 看,创建, 更新ZIP格式的文档附件(zip, jar, apk).也可将资源文件编译成二进制文件,尽管你可能没有直接使用过aapt工具,但是build script

  • ANDROID应用程序的混淆打包分享

    android应用程序的混淆打包1 . 在工程文件project.properties中加入下proguard.config=proguard.cfg , 如下所示:target=android-8 proguard.config=proguard.cfg Eclipse会通过此配置在工程目录生成proguard.cfg文件 2 . 生成keystore (如已有可直接利用) 按照下面的命令行 在D:\Program Files\Java\jdk1.6.0_07\bin>目录下,输入keytoo

  • Android开发apk反编译和二次打包教程

    作为Android开发者,工作中少不了要反编译别人的apk,当然主要目的还是为了学习到更多,取彼之长,补己之短.今天就来总结一下Android反编译和二次打包的一些知识.首先声明本文的目的是为了通过例子讲解反编译和二次打包的原理和方法,继而作为后续讲解防止二次打包和App安全的依据,并不是鼓励大家去重新打包别人的App,盗取他人劳动成果. 本文首先介绍几种Android反编译工具的使用,然后实现在不需要知道源代码的情况下,仅通过修改反编译得到的smali文件实现修改apk逻辑功能的目的. And

  • Android APK反编译图文教程

    在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能会很想知道这些效果界面是怎么去实现的,这时,你便可以对改应用的APK进行反编译查看.下面是我参考了一些文章后简单的教程详解. (注:反编译不是让各位开发者去对一个应用破解搞重装什么的,主要目的是为了促进开发者学习,借鉴好的代码,提升自我开发水平.) 测试环境: win 7 使用工具: 我们下载地址: apktool (资源文件获取)  下载        

  • Android Apk反编译及加密教程

    目录 一.Apk文件组成 二.反编译必备工具及使用 1)apktool 2)Dex2jar 3)jd-gui 三.Apk的加密过程 这几天在上海出差,忙里偷闲学习了一下Apk的反编译工具的基本使用.下面就简单介绍一下如何将我们从网上下载的Apk文件进行反编译得到我们想要获得的资源文件和源码. 一.Apk文件组成 Android的应用程序APK文件说到底也是一个压缩文件,那么可以通过解压缩得打里面的文件内容,不过很显然,当你去解压完去查看的时候,发现里面的很多东西和你想象中的不太一样.资源文件等x

  • 浅谈Android应用安全防护和逆向分析之apk反编译

    概述 这里是Mac环境,如果是window环境的同学,在环境搭建和工具上可以选择Window环境的.先看看需要到的工具: 1.apktool:https://ibotpeaches.github.io/Apktool/install/ 2.dex2jar:https://github.com/pxb1988/dex2jar 3.jd-gui:http://jd.benow.ca 注意:工具一定要是当前最新版本的,否则很容易出现一些莫名其妙的错误. 先看一下项目的包结构 然后在简单看MainAct

  • android apk反编译到java源码的实现方法

    Android由于其代码是放在dalvik虚拟机上的托管代码,所以能够很容易的将其反编译为我们可以识别的代码. 之前我写过一篇文章反编译Android的apk包到smali文件 然后再重新编译签名后打包实现篡改apk的功能. 最近又有一种新的方法来实现直接从Android apk包里的classes.dex文件,把dex码反编译到java的.class二进制码,然后从.class二进制码反编译到java源码想必就不用我来多说了吧. 首先我们需要的工具是dex2jar和jd-gui 其中第一个工具

  • Android APK反编译技巧深入讲解

    导言: 在我们安卓开发当中,我们不仅需要掌握基础的开发技能,也需要掌握软件的安全技能,这样才可以让我们的软件能够成为一款能够真正可以进行发布的软件,同时也可以让自己的核心技术不会被别人所盗取.首先我们应当了解的是,对于反编译我们一共需要三个工具,它们分别是:APKTool,dex2jar,和jd-gui.APKTool:用于解析apk的res文件以及AndroidManifest.xml文件dex2jar:用于把apk解压后生成的classes.dex文件解析为后缀为jar的文件,与下面的jd-

  • 详解Android的反编译和代码混淆

    前言 包括以下内容 要反编译apk需要下面3个工具 反编译资源文件 反编译类文件 代码混淆 要反编译apk需要下面3个工具 1.apktool(资源文件获取) 作用:资源文件获取,可以提取图片文件和布局文件进行使用查看 2.dex2jar(源文件获取) 作用:将APK反编译成java源码(classes.dex转化成jar文件) 3.jd-gui 作用:查看APK中classes.dex转化成的jar文件,即源码文件 下面进行反编译资源文件和类文件: 反编译资源文件 资源文件:包括图片资源.布局

  • 使用android-apktool来逆向(反编译)APK包方法介绍

    谷歌官方提供了apktool可以逆向已经发布出去的APK应用,即反编译已经打包成功的APK文件,使用它可以将其反编译成非常接近打包前的原始格式,对于APK来说,可以具体的逆向AndroidManifest.xml.资源文件resources.arsc以及将dex文件反编译成可以调试的smali文件. Warnning 但apktool并不等于是可以用来侵犯前作者的作品的工具,所以使用apktool工具的用户千万不用用其来进行不正当.非法的使用. It is NOT intended for pi

  • Pyinstaller加密打包成反编译可执行文件

    目录 前言 PYD 打包 说明 前言 了解 Python 的都知道 Pyinstaller 可以将 .py 文件打包成 windows 下可执行的 .exe 文件, 但是在我们不想让他人反编译获取源码的时候就显得无能为力, 此时我们可以了解 .pdy 文件. PYD pyc 是二进制文件, 是由 Python 编译器进行编译后生成的文件, 加载速度快.pyo 是优化编译后的二进制文件, 可以通过 python -O file.py 生成.pyd 则是 Python 的动态链接库, 由其他语言编写

  • Android如何防止apk程序被反编译(尊重劳动成果)

    作为Android应用开发者,不得不面对一个尴尬的局面,就是自己辛辛苦苦开发的应用可以被别人很轻易的就反编译出来. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-sdk-windows\tools\下面多了一proguard文件夹 proguard是一个java代码混淆的工具,通过proguard,别人即使反编译你的apk包,也只会看到一些让人很难看懂的代码,从而达到保护代码的作用. 下面具体说一说怎么样让SDK2.3下的proguard.cfg文件起作用,先

随机推荐