一.逆向目的
之前编写第一个安卓项目时只有账号为admin和密码为123456,两者都对的情况下才会显示登录成功,
现在目的是逆向修改APK,使无论输入什么用户名和密码,都显示"登录成功" ,同时登录成功 字样修改为Login Success

验证成功:

二.工具下载
Apktool 能一步到位完成APK的解包和重新打包,并处理资源文件(AndroidManifest.xml、布局文件等)
shell
#apktool_3.0.2.jar 下载地址
https://github.com/iBotPeaches/Apktool/releases
三.逆向步骤
3.1 工具和apk包放在同一目录
shell
#apktool_3.0.2.jar 和原始包 app-release.apk 本地要有jdk环境
java -jar apktool_3.0.2.jar d app-release.apk
#输出指定位置(可选)
java -jar apktool_3.0.2.jar d app-release.apk -o output_folder
常用解码选项:
-o <目录>:指定输出目录。-f, --force:强制覆盖已存在的输出目录。-r, --no-res:不解码资源文件(只保留resources.arsc)。-s, --no-src:不解码源代码(不生成smali文件)。
执行以上命令会在 当前目录生成 和apk名称相同的文件夹 ,包含 smali 源代码和所有资源文件。

3.2 关于Smali
3.2.1 什么是Smali
Smali就像Android世界的"汇编语言",是一种可读性很好的DEX字节码文本格式。它的核心用途是在应用安全分析和逆向工程中,作为一个强大的中间表示层,让你能读懂并修改应用的底层逻辑 。
3.2.2 Smali应用场景
Smali最主要的应用场景就是Android应用的逆向工程:
- 代码分析与漏洞挖掘:当你想分析一个应用的安全逻辑或查找潜在漏洞,却又没有源码时,Smali是最佳分析对象。它可以清晰展示应用内的所有API调用、算法逻辑和权限声明。
- 应用修改与定制 :比如修改应用的UI布局、跳过启动广告、移除授权验证等。方法通常是修改Smali代码中的判断指令(如
if-eqz改为if-nez)来强制改变程序走向。 - 去广告与解锁高级功能:这也是Smali的一个常见用途。通过修改Smali代码,可以让APP跳过广告播放的请求,或直接让服务端认为用户已付费解锁。
- 动态调试:配合调试工具,你可以在Smali代码层面设置断点、单步执行,动态观察寄存器和变量的变化,深度理解程序运行时的行为。
3.2.3 Smali语法
掌握Smali,可以从以下几个方面入手:
3.2.3.1 数据类型
Smali用特定的字母或格式来表示数据类型。
| Smali标识 | 对应Java类型 |
|---|---|
V |
void |
Z |
boolean |
B |
byte |
S |
short |
C |
char |
I |
int |
J |
long |
F |
float |
D |
double |
Lpackage/name/ObjectName; |
任意对象 |
[I |
int[] (一维数组) |
[[I |
int[][] (二维数组) |
- 对象类型 :以
L开头,后跟完整包名和类名,以分号;结尾,如Ljava/lang/String;表示java.lang.String类。 - 数组 :在类型前加
[,如[I。
3.2.3.2 方法与字段
- 方法签名 :格式是
方法名(参数类型)返回类型,参数间没有分隔符 。onCreate(Landroid/os/Bundle;)V:一个无返回值的onCreate方法,接收Bundle对象。hello(IZ)Ljava/lang/String;:返回String的hello方法,接收int和boolean。
- 字段签名 :格式是
类名;->字段名:字段类型,如Lcom/example/MainActivity;->count:I。
3. 2.3.3 文件结构
一个.smali文件代表一个Java类,内部结构如下:
smali
shell
# 1. 类声明
.class public Lcom/example/MyClass;
# 2. 父类声明
.super Ljava/lang/Object;
# 3. 源文件名(可选)
.source "MyClass.java"
# 4. 字段定义
.field private myField:I
# 5. 方法定义
.method public myMethod(I)V
... # 方法指令
.end method
3.2.3.4 寄存器
Smali代码基于寄存器操作,类似于CPU的寄存器,用于存储数据和执行运算。常用两种类型:
v寄存器(本地寄存器) :方法内局部变量,如v0,v1。p寄存器(参数寄存器) :代表方法的参数。在非静态方法中,p0是this,p1是第一个参数,以此类推。
使用方法前,通常需要声明寄存器总数,例如.registers 2表示使用2个寄存器。
3.2.3.5 常用指令
Smali指令多与寄存器相关,以下是一些核心指令。
- 数据定义指令
const/4 v0, 0x1:将数值1赋值给寄存器v0。
- 数据移动指令
move v1, v0:将v0的值移到v1。move-result v0:获取上一个方法调用的返回值,存入v0。
- 返回指令
return-void:方法无返回值。return v0:返回v0中的值。
- 字段访问指令
iget-object v0, p0, Lcom/example/MyClass;->myField:Ljava/lang/String;:获取p0对象中myField字段的值,存入v0。
- 数组操作指令
aget v0, v1, v2:从v1寄存器指向的数组中,取索引v2的元素,存入v0。
- 运算指令
add-int v2, v0, v1:v2 = v0 + v1(类似地有sub-int等)。
- 方法调用指令
invoke-virtual {v0, v1}, Lcom/example/MyClass;->myMethod(I)V:调用v0对象的myMethod方法,传入参数v1。
- 类型转换指令
check-cast v0, Ljava/lang/String;:检查v0中的对象能否转换为String类型。
- 实例创建指令
new-instance v0, Lcom/example/MyClass;:创建MyClass的新实例,并将引用存入v0。
此外,Smali通过.annotation指令为类、字段或方法添加注解,并保留在DEX文件中。
3.3 Smali 代码
3.3.1 修改代码
打开app-release目录
找到登录逻辑所在的 Smali 文件
shell
smali/com/example/myapplication/MainActivity$1.smali
修改 Smali,让所有情况都跳到成功, 打开 MainActivity$1.smali,找到 onClick 方法(搜索 .method public onClick)
shell
.method public onClick(Landroid/view/View;)V
.locals 3
.line 55
iget-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
invoke-static {p1}, Lcom/example/myapplication/MainActivity;->-$$Nest$fgetetUsername(Lcom/example/myapplication/MainActivity;)Landroid/widget/EditText;
move-result-object p1
invoke-virtual {p1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object p1
invoke-virtual {p1}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object p1
invoke-virtual {p1}, Ljava/lang/String;->trim()Ljava/lang/String;
move-result-object p1
.line 56
iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
invoke-static {v0}, Lcom/example/myapplication/MainActivity;->-$$Nest$fgetetPassword(Lcom/example/myapplication/MainActivity;)Landroid/widget/EditText;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/String;->trim()Ljava/lang/String;
move-result-object v0
# ========== 修改:直接跳转到成功 ==========
goto :goto_success
.line 59
invoke-virtual {p1}, Ljava/lang/String;->isEmpty()Z
move-result v1
if-eqz v1, :cond_0
.line 60
iget-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
const-string v0, "\u8bf7\u8f93\u5165\u7528\u6237\u540d"
const/4 v1, 0x0
invoke-static {p1, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object p1
invoke-virtual {p1}, Landroid/widget/Toast;->show()V
return-void
.line 61
:cond_0
invoke-virtual {v0}, Ljava/lang/String;->isEmpty()Z
move-result v1
if-eqz v1, :cond_1
.line 62
iget-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
const-string v0, "\u8bf7\u8f93\u5165\u5bc6\u7801"
const/4 v1, 0x0
invoke-static {p1, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object p1
invoke-virtual {p1}, Landroid/widget/Toast;->show()V
return-void
.line 63
:cond_1
const-string v1, "admin"
invoke-virtual {p1, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result p1
if-eqz p1, :cond_2
const-string p1, "123456"
invoke-virtual {v0, p1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result p1
if-eqz p1, :cond_2
:goto_success # <--- 新标签,所有 goto 的目标
.line 64
iget-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
# 这里修改 Toast 文字(可选)
const-string v0, "Login Success" # 改成你想要的文字
const/4 v2, 0x0
invoke-static {p1, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object p1
invoke-virtual {p1}, Landroid/widget/Toast;->show()V
return-void
.line 68
:cond_2
iget-object p1, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;
const-string v0, "\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef"
const/4 v2, 0x0
invoke-static {p1, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object p1
invoke-virtual {p1}, Landroid/widget/Toast;->show()V
return-void
.end method

3.3.2 回编译签名
win+r cmd命令 在apktool_3.0.2.jar目录下运行以下命令
shell
# 回编译
java -jar apktool_3.0.2.jar b app-release -o modified.apk
# 签名(若已有 keystore 可跳过生成步骤)
keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000
#签名
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore modified.apk mykey

四.验证

点击modified.apk,雷电模拟器自动打开,若没有安装请提前安装
