

Android 反编译、修改、重打包与签名
source link: https://blog.andiedie.cn/posts/552a/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这篇博客主要介绍如何使用 Apktool、dex2jar、jd-gui等工具反编译 Android APP、修改源码、重新打包并签名。
1. 工具安装
1.1. Apktool
Apktool 主要用于反编译和重编译 APK 文件。
Windows 和 Linux 安装方法参照:官方指南
macOS 安装方法如下:
- 右键保存启动脚本,命名为
apktool
- 下载最新版本的 Apktool,命名为
apktool.jar
- 将
apktool
和apktool.jar
移动到/usr/local/bin
目录下 - 使用
chmod +x apktool
和chmod +x apktool.jar
添加运行权限 - 在命令行直接运行命令
apktool
即可,安装完成
1.2. dex2jar
dex2jar 主要用于将 dex 文件转为 jar 文件
从 GitHub 下载最新版本,并解压。运行解压后目录下的 d2j-dex2jar.sh
或 d2j-dex2jar.bat
即可。
1.3. jd-gui
jd-gui 主要用来可视化 jar 文件。
从 GitHub 下载最新版本,并解压。运行解压后目录下的 JD-GUI
即可。
macOS 下注意:
如果 jd-gui 无法打开,编辑 JD-GUI.app/Contents/MacOS/universalJavaApplicationStub.sh
exec "$JAVACMD" \
-cp "${JVMClassPath}" \
-Xdock:icon="${ResourcesFolder}/${CFBundleIconFile}" \
-Xdock:name="${CFBundleName}" \
## ----- 添加这两行 -----
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED \
--add-opens jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED \
## --------------------
${JVMOptions:+$JVMOptions }\
${JVMDefaultOptions:+$JVMDefaultOptions }\
${JVMMainClass}\
${JVMArguments:+ $JVMArguments}
详见 issue
2. 查看源码
以小米投屏神器 APP 为例。
运行 d2j-dex2jar.sh 将 APK 中的 dex 文件转为 jar 文件:
$ ./d2j-dex2jar.sh mi.apk
dex2jar mi.apk -> ./mi-dex2jar.jar
打开 jd-gui,将 mi-dex2jar.jar
拖进去,即可看到反编译后的源码。
3. 修改源码
这里以增加 Log 为修改源码的例子。
jd-gui 只能查看源码,但是无法修改。要修改源码,只能修改 smali
文件,它类似于汇编,但是要简单很多。
首先得使用 apktool 进行反编译:
apktool d mi.apk -o mi
这里将 mi.apk
进行反编译并将结果放到 mi
目录中。
首先找到需要添加 Log 的位置,建议在 jd-gui 中寻找,然后在 mi
目录中定位。
例如以下 class
和 smali
的对应关系
mi-dex2jar.jar!/com/xiaomi/mitv/phone/tvassistant/b/a.class
mi/smali/com/xiaomi/mitv/phone/tvassistant/b/a.smali
打开 a.class
和 a.smali
,首先看 java
:
public void a(int paramInt)
{
String str1 = String.valueOf(System.currentTimeMillis());
String str2 = a(String.valueOf(paramInt), this.b, str1);
new c(this.d, String.format("http://%s:6095/general?action=setVolum&volum=%d&ts=%s&sign=%s", new Object[] { this.a, Integer.valueOf(paramInt), str1, str2 }), new c.a()
{
public void a(int paramAnonymousInt, String paramAnonymousString) {}
}).d();
}
对应的 smali
文件内容,为了简单,我只提取了部分内容。
.method public a(I)V
# a 是方法,p0 = this
# a 接收一个参数, p1 = paramInt'
# 定义了 8 个寄存器
.locals 8
.prologue
.line 55
# 获取系统当前时间
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
# 结果赋值给 v0
move-result-wide v0
# 调用 String.valueOf 方法,将 v0 转为字符串
invoke-static {v0, v1}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;
# 结果赋值给 v0
move-result-object v0
# 到这一步
# String str1 = String.valueOf(System.currentTimeMillis());
# 执行完了
.line 57
# 调用 String.valueOf 方法 p1 转为字符串
invoke-static {p1}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
# 结果赋值给 v1
move-result-object v1
# 获取 this.b,也就是从 p1 中获取 b,并赋值给 v2
iget-object v2, p0, Lcom/xiaomi/mitv/phone/tvassistant/b/a;->b:Ljava/lang/String;
# 将 v1,v2,v0 作为参数调用方法 a,并传递 p0 作为 this
invoke-direct {p0, v1, v2, v0}, Lcom/xiaomi/mitv/phone/tvassistant/b/a;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
# 将结果赋值给 v1
move-result-object v1
# 到这一步
# String str2 = a(String.valueOf(paramInt), this.b, str1);
# 执行完了
...
假设我们想要得知其中 this.b
的值,可以这样添加 Log:
.method public a(I)V
# 额外定义 4 个寄存器用于存储 TAG
.locals 12
.prologue
.line 55
# 设置初始化TAG
const-string v8, "AndiedieHack.currentTimeMillis"
const-string v9, "AndiedieHack.param"
const-string v10, "AndiedieHack.b"
const-string v11, "AndiedieHack.result"
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v0
invoke-static {v0, v1}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;
move-result-object v0
# 第一个 Log,使用 v8 作为 TAG,输出 v0 的值,即系统当前时间
invoke-static {v8, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 57
invoke-static {p1}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v1
# 第二个 Log,使用 v9 作为 TAG,输出 v1 的值
# v1 是 p1 的字符串形式,p1 是函数参数
invoke-static {v9, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
iget-object v2, p0, Lcom/xiaomi/mitv/phone/tvassistant/b/a;->b:Ljava/lang/String;
# 第三个 Log,使用 v10 作为 TAG,输出 v2 的值,即 this.b
invoke-static {v10, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
invoke-direct {p0, v1, v2, v0}, Lcom/xiaomi/mitv/phone/tvassistant/b/a;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
# 第四个 Log,使用 v11 作为 TAG,输出 v1 的值,即 str2
invoke-static {v11, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
到这一步,源码修改完成。接下来进行重新打包。
3. 重新打包与签名
将修改后的内容重新打包为 APK:
apktool b mi -o unsigned.apk
打包之后的 unsigned.apk
是没有签名的,无法安装。
签名需要使用一个 keystore
,可以直接使用 Android Studio 为我们生成的 debug 用 keystore。
位置在 用户目录/.android/debug.keystore
,alias
是 androiddebugkey
,密码是 android
签名命令:
jarsigner -keystore debug.keystore -signedjar signed.apk unsigned.apk androiddebugkey
输入密码即可。
在手机上安装 signed.apk
,注意如果之前已经安装了投屏神器,需要先卸载,因为两者的签名不一致。
运行 APP,调整音量,可以看到以下 Log:
Recommend
-
98
一、IPA 的简单认识 首先有一个这样的 ipa 包文件: 直接修改文件后缀为: IPADev.zip , 然后解压,是这...
-
65
iOS自动打包主要用xcodebuild命令, 在终端输入xcodebuild --help可以查看xcodebuild的参数。
-
44
我们构建前端项目的时候,往往希望第三方库( vendors )和自己写的代码可以分开打包,因为第三方库往往不需要经常打包更新。对此 Webpack 的文档建议用 CommonsChunkPlugin 来单独打包第三方库...
-
14
APK资源修改与重签名打包 发表于2020-08-10 评论0 2.9k浏览 想免费获取内部独家PPT资料库?观...
-
8
iOS 打包签名,你真的懂吗 2 导语:iOS 签名类型有 Development、AD-Hoc、In-House、App Store,而打包过程中又涉及到各种证书、Provision Profile、entitlements、CertificateSigningRequest、p12、A...
-
8
Maven: 让你的编译和打包自动化发布于 53 分钟前工具与资源中心帮助开发者更加高效的工作,提供围绕开发者全生命周期的工具与资源
-
3
Taro编译打包优化实践发布于 11 月 26 日随着项目越来越大,编译的耗时也在默默地不断增加。无论是开发阶段还是生产集成,编译耗时都成为了一个不容小觑的痛点。
-
7
我们平时都是用 AS 进行打包,这就造成了很多盲点,我们就来看看究竟是咋回事,提前声明这篇文章讲的不全,只讲一些疑惑盲点,需要全面学习的,看老罗的吧,详细的令人发指。 我们从结果入手,看看打包完毕的apk里面是啥模样,把.apk 修改成 .zip...
-
8
apktool 反编译 apk 修改后打包 原创 no_shutdown_ 2022-06-07 11:31:40
-
7
在使用webpack进行项目打包的时候,我们可通过以下方式对不同类型的资源,进行文件名或文件路径的修改1、使用copy-webpack-plugin插件复制的文件,这个就是配置from和tone...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK