android 反编译 重打包,Android 反编译、修改、重打包与签名

这篇博客主要介绍如何使用 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

1

2

3

4

5

6

7

8

9

10

11

12exec "$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}

2. 查看源码

以小米投屏神器 APP 为例。

运行 d2j-dex2jar.sh 将 APK 中的 dex 文件转为 jar 文件:

1

2$ ./d2j-dex2jar.sh mi.apk

dex2jar mi.apk -> ./mi-dex2jar.jar

打开 jd-gui,将 mi-dex2jar.jar 拖进去,即可看到反编译后的源码。

80abe8fc819fd9f8398720d9bbdf532d.png

3. 修改源码

这里以增加 Log 为修改源码的例子。

jd-gui 只能查看源码,但是无法修改。要修改源码,只能修改 smali 文件,它类似于汇编,但是要简单很多。

首先得使用 apktool 进行反编译:

1apktool 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:

1

2

3

4

5

6

7

8

9public 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 文件内容,为了简单,我只提取了部分内容。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37.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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43.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:

1apktool b mi -o unsigned.apk

打包之后的 unsigned.apk 是没有签名的,无法安装。

签名需要使用一个 keystore,可以直接使用 Android Studio 为我们生成的 debug 用 keystore。

位置在 用户目录/.android/debug.keystore,alias 是 androiddebugkey,密码是 android

签名命令:

1jarsigner -keystore debug.keystore -signedjar signed.apk unsigned.apk androiddebugkey

输入密码即可。

4. 测试

在手机上安装 signed.apk,注意如果之前已经安装了投屏神器,需要先卸载,因为两者的签名不一致。

运行 APP,调整音量,可以看到以下 Log:

1306c2810011068378bdd81b327a44f8.png


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部