我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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


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

您可能感兴趣的文章:Android如何防止apk程序被反编译(尊重劳动成果)使用android-apktool来逆向(反编译)APK包方法介绍android apk反编译到java源码的实现方法为Android的apk应用程序文件加壳以防止反编译的教程Android APK反编译图文教程ubuntu环境下反编译android apk的方法Android APK反编译技巧深入讲解


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

作为Android开发者,工作中少不了要反编译别人的apk,当然主要目的还是为了学习到更多,取彼之长,补己之短。今天就来总结一下Android反编译和二次打包的一些知识。首先声明本文的目的是为了通过例子讲解反编译和二次打包的原理和方法,继而
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录