安卓逆向-从分析Dalvik字节码、Dalvik指令集到修改smali代码逻辑
字数 1954 2025-08-09 18:44:09
Android逆向工程:从Dalvik字节码到Smali代码修改
一、Dalvik虚拟机基础
1.1 Dalvik虚拟机概述
Dalvik是Android系统中用于运行应用程序的虚拟机,特点包括:
- 基于寄存器架构(不同于JVM的栈架构)
- 使用dex(Dalvik Executable)文件格式
- 针对移动设备优化(内存占用小、启动快)
1.2 Dalvik字节码特点
- 指令集精简,约200条指令
- 使用16位寄存器(v0-v65535)
- 支持宽指令(wide指令前缀)
- 操作码为1字节(0x00-0xFF)
二、Dalvik指令集详解
2.1 基本指令分类
数据移动指令
move vA, vB- 将vB的值移动到vAmove/from16 vAA, vBBBB- 从大索引寄存器移动move-wide- 移动64位数据move-object- 移动对象引用
数据操作指令
const/4 vA, #+B- 4位常量const/16 vAA, #+BBBB- 16位常量const-string vAA, string@BBBB- 加载字符串
方法调用指令
invoke-virtual- 调用虚方法invoke-super- 调用父类方法invoke-direct- 调用直接方法invoke-static- 调用静态方法invoke-interface- 调用接口方法
控制流指令
if-eq vA, vB, +CCCC- 等于跳转if-ne vA, vB, +CCCC- 不等于跳转goto +AA- 无条件跳转switch- 分支跳转表
2.2 寄存器使用约定
- 参数寄存器:方法参数按顺序使用v0-vn
- 返回值:使用v0或v0-v1(64位)
- 局部变量:从参数寄存器后开始分配
三、Smali语法解析
3.1 Smali文件结构
.class <访问权限> <类名>
.super <父类>
.source <源文件名>
# 字段定义
.field <访问权限> <字段名>:<字段类型>
# 方法定义
.method <访问权限> <方法名>(<参数类型>)<返回类型>
.registers <寄存器数量>
.param <参数定义>
.prologue
# 方法体
.end method
3.2 数据类型表示
- 基本类型:
- V - void
- Z - boolean
- B - byte
- S - short
- C - char
- I - int
- J - long
- F - float
- D - double
- 引用类型:
- Lpackage/name/ObjectName; - 对象
- [I - int数组
- [[Ljava/lang/String; - String二维数组
四、逆向分析流程
4.1 工具链
-
反编译工具:
- apktool - 反编译apk到smali
- dex2jar - dex转jar
- jadx - 直接查看java代码
- baksmali - dex转smali
-
分析工具:
- IDA Pro - 高级反汇编
- JEB - 专业Android逆向工具
- Bytecode Viewer - 多视图分析
4.2 分析步骤
-
使用apktool解包APK:
apktool d target.apk -o output_dir -
分析AndroidManifest.xml确定入口点
-
定位关键代码:
- 搜索关键字符串
- 跟踪Activity跳转
- 分析网络请求相关类
-
阅读smali代码理解逻辑
五、Smali代码修改技术
5.1 常见修改场景
-
绕过验证:
- 修改条件跳转指令
- 强制返回特定值
-
功能注入:
- 插入日志输出
- 添加新方法调用
-
资源修改:
- 修改字符串资源
- 替换图片资源
5.2 修改示例
示例1:修改返回值
原始代码:
.method public isVIP()Z
.registers 2
const/4 v0, 0x0
return v0
.end method
修改后:
.method public isVIP()Z
.registers 2
const/4 v0, 0x1 # 修改为总是返回true
return v0
.end method
示例2:绕过验证
原始代码:
if-eqz v0, :cond_0 # 如果v0==0跳转
invoke-static {v1}, Lcom/example/Check;->valid(Ljava/lang/String;)Z
:cond_0
修改为:
# 直接跳转到验证通过后的代码
goto :cond_0
nop # 保持指令对齐
5.3 修改注意事项
- 保持寄存器数量一致(.registers)
- 注意指令对齐(nop填充)
- 修改后需要重新打包签名:
apktool b output_dir -o modified.apk jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore modified.apk alias_name
六、高级逆向技巧
6.1 动态调试
-
使用adb forward转发调试端口:
adb forward tcp:8700 jdwp:<pid> -
使用jdb或IDEA进行调试
6.2 代码注入
- 使用Xposed框架进行运行时修改
- 使用Frida进行动态hook
6.3 混淆对抗
-
识别常见混淆模式:
- 类名/方法名替换
- 控制流平坦化
- 字符串加密
-
反混淆技术:
- 动态分析获取运行时信息
- 模式识别恢复原始名称
- 自定义脚本处理
七、实战案例
7.1 破解License验证
- 定位License检查方法
- 分析验证逻辑
- 修改关键跳转或返回值
7.2 解锁付费功能
- 查找功能开关方法
- 修改条件判断
- 绕过服务器验证
7.3 分析加密算法
- 定位加密/解密方法
- 提取密钥和算法
- 重建加密逻辑
八、法律与道德规范
- 仅对自有应用或授权应用进行逆向
- 不得用于非法目的
- 尊重知识产权
- 遵守相关法律法规
九、学习资源推荐
-
官方文档:
- Dalvik字节码规范
- Android开发者文档
-
工具资源:
- Apktool官方Wiki
- Smali/Baksmali项目
-
进阶书籍:
- 《Android软件安全与逆向分析》
- 《Android安全攻防权威指南》