关于 smali:3. Smali 与 APK 结构理解
一、APK 解包工具:apktool
, jadx
, dex2jar
当我们拿到一个 APK 文件进行逆向时,通常的目标是:
-
分析 Java 层源码(逻辑、加密、通信等)
-
修改资源或 Smali 代码实现功能修改
-
尝试定位加固方式、脱壳、定位敏感函数
而实现这些目标,需要用到不同的工具来解析 APK 的不同部分。
1.1 工具 1:apktool —— 解包 & 重打包 + Smali 分析
功能:
-
反编译 APK 为 Smali 代码
-
提取
AndroidManifest.xml
和资源文件(res、assets) -
支持修改后再 重新打包 APK
输出内容:
myapp/
├── AndroidManifest.xml ← 已反编译的清晰 XML
├── smali/ ← Smali 代码目录
├── res/ ← layout、drawable 等资源
├── assets/ ← 加密配置、脚本文件等
└── original/ ← META-INF 等签名信息
使用方式:
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 方式(推荐):
jadx-gui myapp.apk
-
可搜索类名、字符串、方法名
-
可定位到 Java 层代码逻辑
-
可导出为整个项目
.java
文件
CLI 导出:
jadx -d output_dir myapp.apk
优点:
-
Java 源码级别还原,阅读体验好
-
快速定位加密函数、网络通信、关键逻辑
-
可搜索代码片段、敏感 API 使用等
缺点:
-
不是 100% 恢复原代码,变量名、结构可能会乱
-
不能直接用于修改和重打包
1.3 工具 3:dex2jar + jd-gui —— 老牌 Java 源码还原工具链
功能:
-
将 DEX 文件转换为
.jar
-
用 Java 反编译工具查看
.jar
的 Java 源码
使用方式:
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(使用
apksigner
或jarsigner
)
1.6 实战举例
实战目的 | 使用方式 |
---|---|
找到某个 API Token 的加载逻辑 | jadx 搜索字符串 → 定位 Java 函数 |
修改登录校验 | apktool 找到 Smali → 修改跳过检查 |
Hook 某个函数 | jadx 找类名函数名 → Frida 脚本注入 |
脱壳分析加固 | apktool 查看 Manifest + Smali → 判断加固方案 |
恢复加密算法实现 | jadx 找 Java 加密逻辑 → Smali 跟踪字段流 |
二、APK 编译 / 反编译流程(反编译到 smali,修改后重新打包)
2.1 整体流程概览
要做的是:
原始 APK → [反编译] → Smali 代码 & 资源文件 → [修改] → [重新打包] → 签名 → 安装运行测试
用的核心工具是:
-
apktool
:负责 APK → Smali & 资源 的反编译、再打包 -
keytool
/apksigner
:APK 签名 -
模拟器或真机:测试打包后的 APK
2.2 反编译 APK 到 Smali
工具:apktool
命令:
apktool d your_app.apk -o your_app_src
反编译结果目录结构:
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 函数:
.method public isLogin()Z.locals 1const/4 v0, 0x1 # 改成返回 truereturn v0
.end method
2.4 重新打包 APK
命令:
apktool b your_app_src -o new_app.apk
这将把修改过的 Smali + 资源重新打包成一个新的 APK。
2.5 给 APK 签名(非常关键)
Android 不允许安装未签名 APK,必须使用 debug 签名或你自己的 keystore。
1)使用 debug 签名快速签名:
# 如果你用的是 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)自己生成签名:
# 生成签名(仅需一次)
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias myalias
签名命令:
apksigner sign --ks my-release-key.jks --out signed.apk new_app.apk
2.6 安装并测试 APK
adb install -r signed.apk
或者将 APK 拖进模拟器中测试(如雷电、夜神、Pixel)。
2.7 常见问题
问题 | 解决方式 |
---|---|
安装失败:未签名 | 一定要使用 apksigner 签名 |
打包失败:资源异常 | 检查 res/ 文件是否手动修改破坏结构 |
安装后闪退 | 使用 logcat 查看崩溃日志 |
找不到类路径 | 检查是否为 smali_classes2 或其他目录 |
2.8 补充技巧:快速调试 Smali 改动
- 给函数添加日志:
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
- 替换函数返回值(绕过登录):
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,路径如下:
your_app/
├── AndroidManifest.xml
Manifest 的主要组成部分
<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"/><!-- 应用声明 --><applicationandroid: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 快速定位入口类?
<activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter>
</activity>
主界面是 .MainActivity
,真实路径是:
smali/com/example/app/MainActivity.smali
3.2 res 目录资源文件分析
资源文件主要用于界面布局、文字、图片等。路径:
your_app/
├── res/
│ ├── layout/ # XML 界面布局
│ ├── values/ # 字符串、颜色、样式等
│ ├── drawable*/ # 图片资源
│ ├── mipmap*/ # 启动图标等
layout 目录(布局 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 目录(字符串/样式)
<!-- res/values/strings.xml -->
<resources><string name="app_name">极验验证器</string><string name="login_title">请输入账号密码</string>
</resources>
-
用于分析文本提示、隐藏字符串等
-
Java/Smali 中引用方式:
@string/app_name
→R.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 中的使用方式:
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_SMS 、RECORD_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 文件的结构如下图:
+--------------------+
| 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代码
public int sum(int a, int b) {return a + b;
}
编译成 DEX(字节码):
[method_id] sum (II)I
[proto_id] params: int, int → return: int
[code_item] add-int v0, p1, p2
反编译成 Smali:
.method public sum(II)I.locals 1add-int v0, p1, p2return 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、反调试、脱壳等 |