[安卓逆向]逆向第一个安卓程序(二)

一.逆向目的

之前编写第一个安卓项目时只有账号为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;:返回Stringhello方法,接收intboolean
  • 字段签名 :格式是 类名;->字段名:字段类型,如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寄存器(参数寄存器) :代表方法的参数。在非静态方法中,p0thisp1是第一个参数,以此类推。
    使用方法前,通常需要声明寄存器总数,例如.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, v1v2 = 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,雷电模拟器自动打开,若没有安装请提前安装

相关推荐
sN2vuQ08W2 小时前
uni-app 实现视频聊天、屏幕分享,支持Android、HarmonyOS、iOS
android·uni-app·音视频
Mem0rin2 小时前
[LLM基础] Transformer 库的使用
android·深度学习·transformer
UXbot2 小时前
轻量级原型工具如何支持Web应用的完整设计到开发链路
android·前端·人工智能·ios·交互·ui设计
帅次2 小时前
Jetpack Compose 焦点与键盘:FocusRequester、imePadding 与 BringIntoView 实战
android·android studio·android jetpack·android runtime
曼岛_4 小时前
[安卓逆向]编写第一个安卓项目(一)
android·安卓逆向
rocpp16 小时前
Android 相册选择与拍照接入实践:MediaStore 分页、权限适配与 FileProvider
android
Flynt16 小时前
升级Flutter 3.44,我踩了HCPP和AGP 9的坑
android·flutter·dart
白色牙膏17 小时前
Cocos Creator 2.4.x 接入 AdMob 插件的迁移实践
android
我命由我1234519 小时前
C++ - 面向对象 - 常成员函数
android·java·linux·c语言·开发语言·c++·算法