关于 smali:3. Smali 与 APK 结构理解

一、APK 解包工具:apktool, jadx, dex2jar

当我们拿到一个 APK 文件进行逆向时,通常的目标是:

  • 分析 Java 层源码(逻辑、加密、通信等)

  • 修改资源或 Smali 代码实现功能修改

  • 尝试定位加固方式、脱壳、定位敏感函数

而实现这些目标,需要用到不同的工具来解析 APK 的不同部分。

1.1 工具 1:apktool ------ 解包 & 重打包 + Smali 分析

功能:

  • 反编译 APK 为 Smali 代码

  • 提取 AndroidManifest.xml 和资源文件(res、assets)

  • 支持修改后再 重新打包 APK

输出内容:

bash 复制代码
myapp/
├── AndroidManifest.xml       ← 已反编译的清晰 XML
├── smali/                    ← Smali 代码目录
├── res/                      ← layout、drawable 等资源
├── assets/                   ← 加密配置、脚本文件等
└── original/                 ← META-INF 等签名信息

使用方式:

bash 复制代码
apktool d myapp.apk -o myapp
apktool b myapp -o newapp.apk

优点:

  • 结构清晰,可修改资源文件和 Smali

  • 支持重打包,非常适合二次修改

  • 可搭配签名工具重新签名

缺点:

  • 无法恢复 Java 源码(Smali 是汇编级中间代码)

1.2 工具 2:jadx ------ Java 源码查看神器(GUI + CLI)

功能:

  • 将 DEX 文件反编译为 Java 源码

  • 提供 GUI 界面浏览、搜索类、方法、字符串

  • 也可以命令行导出所有 Java 代码

使用方式:

GUI 方式(推荐):

bash 复制代码
jadx-gui myapp.apk
  • 可搜索类名、字符串、方法名

  • 可定位到 Java 层代码逻辑

  • 可导出为整个项目 .java 文件

CLI 导出:

bash 复制代码
jadx -d output_dir myapp.apk

优点:

  • Java 源码级别还原,阅读体验好

  • 快速定位加密函数、网络通信、关键逻辑

  • 可搜索代码片段、敏感 API 使用等

缺点:

  • 不是 100% 恢复原代码,变量名、结构可能会乱

  • 不能直接用于修改和重打包

1.3 工具 3:dex2jar + jd-gui ------ 老牌 Java 源码还原工具链

功能:

  • 将 DEX 文件转换为 .jar

  • 用 Java 反编译工具查看 .jar 的 Java 源码

使用方式:

bash 复制代码
dex2jar myapp.apk -o myapp.jar

然后用 jd-gui 打开生成的 JAR 文件查看源码。

优点:

  • 最早的 Java 源码查看方式之一

  • jd-gui 查看变量、函数较直观

缺点:

  • dex2jar 在处理多 dex / 新版 APK 时容易出错

  • jd-gui 不如 jadx-gui 强大(不能搜索,UI 老旧)

1.4 工具之间的配合策略

目标 使用工具 说明
阅读 Java 代码 jadx-gui 推荐第一步,快速理解逻辑
修改资源或代码 apktool 提取 smali/res/,修改再打包
深度分析/自动脚本 apktool + frida Smali Hook、定位类名
压缩包分析(手动) unzip APK 解压 apk 查看 META-INF / assets 手工分析
生成 .jar 查看 dex2jar + jd-gui 不推荐主力使用,适合老项目

1.5 推荐的完整工作流

  • apktool d myapp.apk → 拿到 Smali + 资源 + Manifest

  • jadx-gui myapp.apk → 找 Java 关键函数、参数、逻辑

  • 分析混淆/加密 → 对照 Smali 和 Java 定位加壳/关键代码

  • 修改 Smali 或资源 → 用 apktool 修改后 b 打包

  • 重新签名 APK (使用 apksignerjarsigner

1.6 实战举例

实战目的 使用方式
找到某个 API Token 的加载逻辑 jadx 搜索字符串 → 定位 Java 函数
修改登录校验 apktool 找到 Smali → 修改跳过检查
Hook 某个函数 jadx 找类名函数名 → Frida 脚本注入
脱壳分析加固 apktool 查看 Manifest + Smali → 判断加固方案
恢复加密算法实现 jadx 找 Java 加密逻辑 → Smali 跟踪字段流

二、APK 编译 / 反编译流程(反编译到 smali,修改后重新打包)

2.1 整体流程概览

要做的是:

bash 复制代码
原始 APK → [反编译] → Smali 代码 & 资源文件 → [修改] → [重新打包] → 签名 → 安装运行测试

用的核心工具是:

  • **apktool:**负责 APK → Smali & 资源 的反编译、再打包

  • **keytool / apksigner:**APK 签名

  • 模拟器或真机:测试打包后的 APK

2.2 反编译 APK 到 Smali

工具:apktool

命令:

bash 复制代码
apktool d your_app.apk -o your_app_src

反编译结果目录结构:

bash 复制代码
your_app_src/
├── AndroidManifest.xml   ← Android 应用的配置文件(反编译后的 XML)
├── smali/                ← 主 dex 的 Smali 代码
├── smali_classes2/       ← 第二个 dex(如有)
├── res/                  ← 应用 UI 资源
├── assets/               ← 各种配置、脚本、加密数据
├── unknown/              ← 非标准结构
└── original/             ← 原始 META-INF 信息等

2.3 修改 Smali 或资源

可以修改的内容包括:

内容 路径 示例
Java逻辑 smali/ 修改跳过登录校验
字符串 res/values/strings.xml 修改 App 名
布局 res/layout/ 改按钮显示
Manifest AndroidManifest.xml 增加权限等

示例:跳过登录逻辑

找到如下 Smali 函数:

bash 复制代码
.method public isLogin()Z
    .locals 1
    const/4 v0, 0x1    # 改成返回 true
    return v0
.end method

2.4 重新打包 APK

命令:

bash 复制代码
apktool b your_app_src -o new_app.apk

这将把修改过的 Smali + 资源重新打包成一个新的 APK。

2.5 给 APK 签名(非常关键)

Android 不允许安装未签名 APK,必须使用 debug 签名或你自己的 keystore。

1)使用 debug 签名快速签名:

bash 复制代码
# 如果你用的是 Android SDK,自带 debug.keystore
apksigner sign --ks ~/.android/debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out signed_new_app.apk new_app.apk

2)自己生成签名:

bash 复制代码
# 生成签名(仅需一次)
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myalias

签名命令:

bash 复制代码
apksigner sign --ks my-release-key.jks --out signed.apk new_app.apk

2.6 安装并测试 APK

bash 复制代码
adb install -r signed.apk

或者将 APK 拖进模拟器中测试(如雷电、夜神、Pixel)。

2.7 常见问题

问题 解决方式
安装失败:未签名 一定要使用 apksigner 签名
打包失败:资源异常 检查 res/ 文件是否手动修改破坏结构
安装后闪退 使用 logcat 查看崩溃日志
找不到类路径 检查是否为 smali_classes2 或其他目录

2.8 补充技巧:快速调试 Smali 改动

  • 给函数添加日志:
bash 复制代码
const-string v0, "TAG"
const-string v1, "Login function called!"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
  • 替换函数返回值(绕过登录):
bash 复制代码
const/4 v0, 0x1
return v0
  • 强制跳转 Activity(改 Intent)

2.9 小结

  • apktool 反编译 APK 为 Smali + 资源

  • 修改 Smali 或资源文件以实现绕过或定制

  • 使用**apktool b** 打包、apksigner 签名

  • 安装并调试新 APK,结合 logcat 分析


三、Manifest 与资源文件分析

3.1 AndroidManifest.xml 分析详解

APK 中的**AndroidManifest.xml** 是核心配置文件,控制应用的权限、组件、入口点等信息。通过 apktool 反编译后,它会变成可读的 XML,路径如下:

bash 复制代码
your_app/
├── AndroidManifest.xml

Manifest 的主要组成部分

XML 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app"
    android:versionCode="10"
    android:versionName="1.0.0">

    <!-- 权限声明 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_SMS"/>

    <!-- 特性声明 -->
    <uses-feature android:name="android.hardware.camera" android:required="false"/>

    <!-- 应用声明 -->
    <application
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:allowBackup="true">

        <!-- 主入口 Activity -->
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <!-- 其他组件 -->
        <activity android:name=".LoginActivity"/>
        <service android:name=".MyService"/>
        <receiver android:name=".AlarmReceiver"/>
        <provider android:name="androidx.core.content.FileProvider"
                  android:authorities="com.example.app.fileprovider"
                  android:exported="false"/>
    </application>
</manifest>

Manifest 可分析的关键点

项目 作用 用途
package 应用包名 定位主类、资源路径
<uses-permission> 声明权限 判断敏感操作(如读短信、网络、相机)
<application> 应用入口 看启动配置、资源入口
<activity> 声明 Activity 找到界面跳转逻辑入口
<service> 后台服务 检查是否有后台恶意逻辑
<receiver> 广播监听 是否监听短信、系统事件等
<provider> 文件或数据库访问接口 判断是否有导出风险点

示例:如何通过 Manifest 快速定位入口类?

XML 复制代码
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

主界面是**.MainActivity**,真实路径是:

Swift 复制代码
smali/com/example/app/MainActivity.smali

3.2 res 目录资源文件分析

资源文件主要用于界面布局、文字、图片等。路径:

bash 复制代码
your_app/
├── res/
│   ├── layout/           # XML 界面布局
│   ├── values/           # 字符串、颜色、样式等
│   ├── drawable*/        # 图片资源
│   ├── mipmap*/          # 启动图标等

layout 目录(布局 XML)

XML 复制代码
<!-- res/layout/activity_login.xml -->
<LinearLayout ... >
    <TextView android:text="@string/login_title"/>
    <EditText android:id="@+id/username"/>
    <EditText android:id="@+id/password"/>
    <Button android:onClick="doLogin"/>
</LinearLayout>
  • 可以帮助你快速识别按钮/控件绑定的方法名

  • 比如**onClick="doLogin"LoginActivity.smali** 里找 doLogin 方法

values 目录(字符串/样式)

XML 复制代码
<!-- res/values/strings.xml -->
<resources>
    <string name="app_name">极验验证器</string>
    <string name="login_title">请输入账号密码</string>
</resources>
  • 用于分析文本提示、隐藏字符串等

  • Java/Smali 中引用方式:@string/app_nameR.string.app_name

drawable/mipmap(图标/图片资源)

  • drawable 通常为按钮背景、状态图标

  • mipmap 是 APP 图标**(ic_launcher.png)**

可用工具查看其中是否嵌入敏感信息,比如二维码、恶意图标伪装等。

3.3 资源 ID 映射机制(R.java 与 Smali 的关系)

  • Java 中:R.layout.activity_login

  • 编译后:0x7f0a0010(整数资源 ID)

  • 在 Smali 中的使用方式:

bash 复制代码
const v0, 0x7f0a0010
invoke-virtual {p0, v0}, Lcom/example/app/MainActivity;->setContentView(I)V

可以通过 jadx-gui 打开 APK 找到 R.java 对应的映射关系来反查 ID。

实战分析思路总结

场景 Manifest 线索 res 线索 后续操作
找主界面 <intent-filter>MainActivity layout XML 中 onClick 函数 进入 smali 分析函数
判断功能权限 uses-permission 中包含 READ_SMSRECORD_AUDIO 检查是否监听短信/语音等
监听短信广播 <receiver>SmsReceiver 分析是否有钓鱼或信息拦截
定位文件导出风险点 <provider android:exported="true"> 判断是否有任意文件读取风险点
找图形验证码组件 layout 中 ImageView、Button layout/activity_login.xml 分析点击事件和图像加载逻辑

3.4 小结:Manifest + 资源分析的意义

  • 快速定位关键组件入口(Activity/Service/Receiver)

  • 判断敏感权限申请与组件导出(安全评估)

  • 配合布局文件找 UI 操作对应方法(逆向入口)

  • 结合 Smali 对照分析逻辑代码


四、Dex 文件格式基础(了解 Smali 背后是 Dalvik bytecode)

什么是 DEX 文件?

  • DEX 是 Android 平台用于执行的二进制格式的字节码文件,由 Java 编译而来。

  • 一个 APK 中可能有一个或多个 DEX 文件(比如 classes.dex, classes2.dex 等)。

  • 它是 Dalvik 虚拟机(早期 Android)ART(Android Runtime) 执行的对象。

4.1 DEX 文件的整体结构

一个 DEX 文件的结构如下图:

bash 复制代码
+--------------------+
| Header             |  # dex 文件头部
+--------------------+
| String IDs         |  # 所有字符串索引表
+--------------------+
| Type IDs           |  # 所有类型(类)索引表
+--------------------+
| Proto IDs          |  # 所有方法签名(参数/返回类型)
+--------------------+
| Field IDs          |  # 所有字段(类成员变量)
+--------------------+
| Method IDs         |  # 所有方法的签名
+--------------------+
| Class Definitions  |  # 每个类的结构体定义
+--------------------+
| Data Section       |  # 所有字节码、常量、注解等原始数据
+--------------------+

所有这些部分都以**"索引+引用+数据"**的方式来组织,这就解释了为什么在 Smali 里经常看到 .field, .method, .proto, .string, .type 等信息。

4.2 关键字段详细说明

段名 说明 对应 Smali
Header 包含 magic、版本、校验和等信息 与 Smali 无直接关系
String IDs 所有字符串常量,如类名、方法名、字段名 .method, .field 中用到的字符串
Type IDs 所有类型(如 Ljava/lang/String; 所有类/参数类型
Proto IDs 方法的签名(参数类型+返回值) 方法声明部分 .method public test(II)V
Field IDs 类字段:所属类 + 名称 + 类型 .field public name:Ljava/lang/String;
Method IDs 方法签名(所属类 + 名称 + 签名) .method public test(I)V
Class Defs 每个类的信息:字段、方法、超类等 每个 .smali 文件
Data Section 字节码、注解、数组、调试信息 .locals, 指令等都是来自这里

4.3 从 Java 到 DEX 到 Smali 的关系图

java 复制代码
// Java代码
public int sum(int a, int b) {
    return a + b;
}

编译成 DEX(字节码):

bash 复制代码
[method_id] sum (II)I
[proto_id]  params: int, int → return: int
[code_item] add-int v0, p1, p2

反编译成 Smali:

bash 复制代码
.method public sum(II)I
    .locals 1
    add-int v0, p1, p2
    return v0
.end method

可以看到:

DEX 字节码结构 Smali 表现 说明
method_id .method 方法定义
proto_id (II)I 参数签名
code_item 指令块 add-int / return
field_id .field 成员变量
string_id 方法名、字段名、类名 被引用的字符串

4.4 DEX 中的字节码与 Smali 指令对应

Smali 指令 DEX 字节码行为 说明
const v0, 1 加载常量 立即数加载
move v0, p1 寄存器移动 值拷贝
add-int v0, v1, v2 两数相加 算术操作
return v0 方法返回 指定值返回
invoke-virtual {p0}, Lxxx;->method()V 虚方法调用 方法跳转
iget v0, p0, Lxxx;->field:I 获取成员字段 对象字段读取
sput v0, Lxxx;->staticField:I 写静态字段 静态变量写入

4.5 为什么需要理解DEX 格式?

  • 更深入理解 Smali 指令意义(知道它来源于哪段 DEX 字节码)

  • 定位问题更方便(某个类 Smali 错误,找其 DEX 结构)

  • 参与二进制级分析(脱离 Smali,直接 patch DEX)

  • 配合 tools(baksmali、dexdump、dex2jar)分析数据段、方法偏移等

4.6 小结

内容 意义
DEX 是 Dalvik/ART 的执行格式 所有 Smali 指令来源
每个类在 DEX 中有一个 ClassDef Smali 对应一个 class
所有调用/变量/类/方法 都是"索引+引用"机制 提高执行效率和体积压缩
DEX 格式理解有助于进阶分析 DEX Patch、反调试、脱壳等
相关推荐
饕餮争锋10 分钟前
单点登陆(SSO)简介-笔记
java·笔记
行星0081 小时前
docker常用命令
java·云原生·eureka
破刺不会编程1 小时前
Linux中基础IO(下)
linux·运维·服务器·开发语言
阮少年、1 小时前
Course 1: Best Practice of RK‘s start Maps SDK for javascript
开发语言·javascript·ecmascript
magic 2451 小时前
实时同步缓存,与阶段性同步缓存——补充理解《补充》
java·redis·mysql
牛马baby1 小时前
Java高频面试之并发编程-23
java·开发语言·面试
hqxstudying2 小时前
Redis击穿,穿透和雪崩详解以及解决方案
java·数据库·redis·缓存
玹之又玹2 小时前
Kafka 客户端连接机制的一个典型陷阱
java·kafka
白初&2 小时前
CVE-2024-27348
java·安全
五步晦暝3 小时前
【VBA 中GetOpenFilename】常用友好的人机交互文件全路径选择模式
开发语言·人机交互