Android反编译和二次打包
参考:APK反编译
一、工具介绍:
1、解压工具
2、JDK
3.apktool: aapt.exe,apktool.bat,apktool.jar;三个在同一目录结合使用,用来反编译apk,反编译生成smali字节码文件,提取apk中的资源文件,apk重新打包。
4.dex2jar:该工具作用是将classes.dex文件,翻译出程序的源代码、图片、XML配置、语言资源等文件(如果apk未加固),反编译出文件,使用jd-gui工具进行查看;
5.jarsigner.exe:签名工具,将重新打包的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。
1、反编译apk包。利用apk tool对apk进行反编译。apktool -f [待反编译的apk] -o [反编译之后存放文件夹]
运行apktool.jar这个jar文件来将apk文件进行反编译,在java中,运行可执行jar包的命令是:
java -jar jar包名.jar
命令:java -jar apktools.jar d -f -o test_output_dir ****.apk
此命令将会反编译apk包到test_output_dir文件夹下。文件夹下是反编译出来的各种文件夹。具体参考网上找的图:

2、使用dex2jar反编译apk得到Java源代码。首先将apk包,解压成zip文件。得到其中的classes.dex文件(它就是java文件编译再通过dx工具打包而成的)
d2j-dex2jar classes.dex
命令执行完成之后,在当前目录下就可以看到生成的Jar文件了。
3、可以用jd-gui工具进行查看步骤2内生成的classes_dex2jar.jar。其内容和步骤1内的smali相对应。
被混淆过的类文件名称以及里面的方法名称都会以a,b,c....之类的样式命名

可以看到反编译的代码和原本的代码差别不大,主要差别是原来的资源引用全都变成了数字。
下面我们来修改这个apk的内容。
4、在步骤1内生成的文件夹内,找到主要的类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 方法
5、然后新建一个工程,在这个工程中实现想要替换的代码,我们这里是希望将原始工程中打印日志的地方替换为弹出一个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文件。
6、最后还要记得对生成的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文件进行替换。这样的修改方式降低了修改的难度也减小了犯错误的风险。
Android反编译和二次打包的更多相关文章
- Android反编译odex然后重新打包
#Android反编译odex然后重新打包 最近不知道怎么回事,突然把我那刷了氧OS的root了,然后就开始好奇起来氢OS所带有的那些本地化的东西,比如通话录音就是典型的一个之一.其中也做了很多的尝试 ...
- Android反编译apk并重新打包签名(Mac环境)
工具下载 apktool :https://ibotpeaches.github.io/Apktool/install dex2jar:https://github.com/pxb1988/dex2j ...
- Android反编译(二)之反编译XML资源文件
Android反编译(二) 之反编译XML资源文件 [目录] 1.工具 2.反编译步骤 3.重新编译APK 4.实例 5.装X技巧 6.学习总结 1.工具 1).反编译工具 apktool http ...
- Android APK反编译(二)
参考:APK反编译 工具介绍 apktool 作用:资源文件获取,可以提取出图片文件和布局文件进行使用查看 dex2jar 作用:将apk反编译成java源码(classes.dex转化成jar文件) ...
- Android 反编译apk 详解
测试环境: win 7 使用工具: CSDN上下载地址: apktool (资源文件获取) 下载 dex2jar(源码文件获取) 下载 jd-gui (源码查看) ...
- Android反编译教程
本文摘自 http://blog.csdn.net/ithomer/article/details/6727581 本文Android反编译教程,测试环境: Win7 Ultimate x64 Ubu ...
- Android反编译(三)之重签名
Android反编译(三) 之重签名 [目录] 1.原理 2.工具与准备工作 3.操作步骤 4.装X技巧 5.问题 1.原理 1).APK签名的要点 a.所有的应用程序都必须有数字证书 ,Androi ...
- Android反编译(一)之反编译JAVA源码
Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具 dex2jar http://code.go ...
- Android反编译工具的使用-Android Killer
今天百度搜索“Android反编译”搜索出来的结果大多数都是比较传统的教程.刚接触反编译的时候,我也是从这些教程慢慢学起的.在后来的学习过程中,我接触到比较方便操作的Android反编译.在这,我将使 ...
随机推荐
- mongodb的几种启动方法
1 mongodb的几种启动方法 启动Mongodb服务有两种方式,前台启动或者Daemon方式启动,前者启动会需要保持当前Session不能被关闭,后者可以作为系统的fork进程执行,下文中的p ...
- hdu 1272 小希的迷宫【并查集】
<题目链接> 小希的迷宫 Problem Description 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的 ...
- Linux学习之文本处理命令(五)
---恢复内容开始--- Linux 系统之文本处理命令 (一)基于关键字搜索 (二)基于列处理文本 (三)文本统计 (四)文本排序 (五)删除重复行 (六)文本比较 (七)处理文本内容 (八)搜索替 ...
- LoRaWAN 1.1 网络协议规范 - 1 引言
LoRaWAN 1.1 网络协议规范 LoRaWAN 1.1 版本封稿很久了也没有完整啃过一遍,最近边啃边翻译,趁着这个机会把它码下来. 如果觉得哪里有问题,欢迎留言斧正. 翻译不易,转载请申明出处和 ...
- 线程、对称多处理和微内核(OS 笔记三)
线程.对称多处理 这一部分继续深入探讨与进程管理相关的高级概念并了解多处理机的对称多处理技术. 进程和线程 到目前为止提出的进程的概念包含两个特点: 资源所有权 存放进程映像的虚拟地址空间 调度/ ...
- C# DataGridView插入DB
public static bool ContrastColumns(DataColumnCollection co1, DataGridViewColumnCollection co2) { boo ...
- HDU 5628 Clarke and math——卷积,dp,组合
HDU 5628 Clarke and math 本文属于一个总结了一堆做法的玩意...... 题目 简单的一个式子:给定$n,k,f(i)$,求 然后数据范围不重要,重要的是如何优化这个做法. 这个 ...
- loj#2552. 「CTSC2018」假面
题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的 ...
- 洛谷.T22136.最长不下降子序列(01归并排序 分治)
题目链接 \(Description\) 给定一个长为n的序列,每次可以反转 \([l,r]\) 区间,代价为 \(r-l+1\).要求在\(4*10^6\)代价内使其LIS长度最长,并输出需要操作的 ...
- Python3练习题系列(03)
题目: 思考While循环,看看它的特点是什么? 知识点: while循环 分析: 特点:while-loop(while 循环).while-loop 会一直执行它下面的代码片段,直到它对应的布尔表 ...