移动安全Android逆向系列:Dalvik概念&破解实例
字数 6500 2025-08-09 15:23:13
Android逆向工程:Dalvik虚拟机与破解实例详解
一、Dalvik虚拟机基础
1. Dalvik概述
Dalvik是Google专为Android设计的虚拟机,具有以下特点:
- 基于寄存器架构(不同于JVM的基于栈架构)
- 使用专属的dex文件格式(Dalvik Executable)
- 执行效率更高,占用空间更少
2. 寄存器命名法
Dalvik有两种寄存器命名方式:
v命名法
- 所有寄存器从v0开始递增命名
- 参数寄存器:v(m-n)~vm(m为总寄存器数,n为参数数)
- 局部变量寄存器:v0~vn
p命名法
- 参数寄存器:p0~pn
- 局部变量寄存器:v0~vn
3. Smali代码分析
Smali是dex文件的可读形式,是Dalvik的字节码表示:
方法签名示例
static public DecryptDemo->getHelloWorld(Ljava/lang/String;I)Ljava/lang/String;
- 方法名:getHelloWorld
- 参数类型:Ljava/lang/String;和I
- 返回值类型:Ljava/lang/String;
寄存器使用示例
invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
- 调用StringBuilder的append方法
- 参数位于p0,结果存入v1
4. 反编译工具链
编译/反编译流程及工具:
-
编译流程:
- .java → .class → .dex → .smali
- 使用dx工具打包:
dx --dex --output=Test.dex com/xxx/ooo/Test.class
-
反编译流程:
- 使用baksmali反编译:
java -jar baksmali.jar -o smali_out/ source.dex - 使用smali重新打包:
java -jar smali.jar smali_out/ -o source.dex
- 使用baksmali反编译:
5. Dalvik字节码类型
| 类型描述符 | Java类型 |
|---|---|
| V | void |
| Z | boolean |
| B | byte |
| S | short |
| C | char |
| I | int |
| J | long |
| F | float |
| D | double |
| L | Java对象类型 |
| [ | 数组类型 |
对象类型表示:Ljava/lang/String;(全限定名用"/"代替".")
二、Dalvik指令集详解
1. 数据定义指令
| 指令 | 描述 |
|---|---|
| const/4 vA,#+B | 将4位数值符号扩展为32位后赋值给vA |
| const-wide/16 vAA,#+BBBB | 将16位数值符号扩展为64位后赋值给vAA |
| const/high16 vAA,#+BBBB0000 | 将数值右补零扩展为32位后赋给vAA |
| const-string vAA,string@BBBB | 通过字符串索引获取字符串赋值给vAA |
| const-class vAA,type@BBBB | 通过类型索引获取类引用赋值给vAA |
2. 数据操作指令
| 指令 | 描述 |
|---|---|
| move vA,vB | 将vB的值赋给vA |
| move/from16 vAA,vBBBB | 将16位vBBBB的值赋给7位vAA |
| move/16 vAAAA,vBBBB | 将16位vBBBB的值赋给16位vAAAA |
| move-object vA,vB | 将vB的对象引用赋给vA |
| move-result vAA | 将上一个invoke指令的32位非对象结果赋给vAA |
| move-result-wide vAA | 将上一个invoke指令的64位非对象结果赋给vAA |
| move-result-object vAA | 将上一个invoke指令的对象结果赋给vAA |
| move-exception vAA | 保存上一个运行时异常到vAA |
3. 比较指令
| 指令 | 描述 |
|---|---|
| cmpl-float vAA,vBB,vCC | 比较单精度浮点数:vBB>vCC→-1, ==→0, <→1 |
| cmpg-float vAA,vBB,vCC | 比较单精度浮点数:vBB>vCC→1, ==→0, <→-1 |
| cmpl-double vAA,vBB,vCC | 比较双精度浮点数:vBB>vCC→-1, ==→0, <→1 |
| cmpg-double vAA,vBB,vCC | 比较双精度浮点数:vBB>vCC→1, ==→0, <→-1 |
4. 跳转指令
| 指令 | 描述 |
|---|---|
| goto +AA | 无条件跳转到指定偏移处 |
| packed-switch vAA,+BBBBBBBB | 有规律分支跳转,vAA为判断值,BBBBBBBB为偏移表索引 |
| sparse-switch vAA,+BBBBBBBB | 无规律分支跳转 |
| if-eq vA,vB,target | 等价于if(a==b) |
| if-ne vA,vB,target | 等价于if(a!=b) |
| if-lt vA,vB,target | 等价于if(a<b) |
| if-gt vA,vB,target | 等价于if(a>b) |
| if-ge vA,vB,target | 等价于if(a>=b) |
| if-le vA,vB,target | 等价于if(a<=b) |
5. 方法调用指令
| 指令 | 描述 |
|---|---|
| invoke-virtual | 调用实例的虚方法(普通方法) |
| invoke-super | 调用实例的父类方法 |
| invoke-direct | 调用实例的直接方法 |
| invoke-static | 调用静态方法 |
| invoke-interface | 调用接口方法 |
6. 实例操作指令
| 指令 | 描述 |
|---|---|
| new-instance vAA,type@BBBB | 构造指定类型对象并将引用赋给vAA |
| instance-of vA,vB,type@CCCC | 判断vB引用是否为指定类型,是则v1=1,否则v1=0 |
| check-cast vAA,type@BBBB | 将vAA引用转为指定类型,失败则抛出ClassCastException |
三、Android四大组件
1. Activity
- 功能:提供用户界面
- 生命周期方法:
- onCreate():创建Activity
- onStart():Activity可见
- onResume():获取焦点,可交互
- onPause():失去焦点
- onStop():Activity不可见
- onDestroy():销毁Activity
- onRestart():从停止状态恢复
2. Service
- 功能:提供后台服务
- 生命周期方法:
- onCreate():首次创建服务
- onStart():启动服务(已过时,改用onStartCommand)
- onDestroy():销毁服务
3. BroadcastReceiver
- 功能:接收和响应系统级广播
- 特点:轻量级,生命周期短暂
4. ContentProvider
- 功能:实现应用间数据共享
- 实现方式:
- 使用现有ContentProvider访问数据
- 创建自定义ContentProvider提供数据接口
四、破解实例:贪吃蛇APK内购破解
1. 目标分析
- 原始行为:点击购买显示"支付失败"
- 破解目标:修改为"支付成功"实现免费购买
2. 工具准备
- Jadx-gui:反编译APK查看Java代码
- Android Killer:反编译为smali代码并修改
3. 破解步骤
-
使用Jadx搜索关键字符串:
- 搜索"支付失败"定位到BuyFailed()方法
- 搜索"支付成功"定位到BuySuccess()方法
-
分析smali代码:
- 使用Android Killer反编译APK
- 定位到支付失败和成功的smali代码段
-
代码替换:
- 将BuySuccess()的smali代码复制
- 替换BuyFailed()的原有代码
-
回编译:
- 保存修改后的smali代码
- 使用Android Killer重新打包APK
-
验证效果:
- 安装修改后的APK
- 确认点击购买显示"支付成功"
4. 关键smali代码示例
原始支付失败代码:
.method public BuyFailed()V
... 失败处理逻辑 ...
.end method
修改为调用成功:
.method public BuyFailed()V
invoke-virtual {p0}, Lcom/example/game/SnakeActivity;->BuySuccess()V
return-void
.end method
五、总结
本教程详细介绍了Android逆向工程的核心知识:
- Dalvik虚拟机:架构特点、寄存器命名、smali语法
- Dalvik指令集:数据操作、流程控制、方法调用等关键指令
- Android组件:四大组件及其生命周期
- 实战破解:通过修改smali代码实现内购破解的完整流程
掌握这些知识后,可以:
- 分析Android应用的运行机制
- 定位关键代码进行修改
- 实现简单的功能破解
- 为更复杂的逆向工程打下基础
建议进一步学习:
- 更复杂的smali代码分析
- 动态调试技术
- 加固应用的脱壳技术
- 原生库(so文件)的逆向分析