fatalSec/DaliVM
GitHub: fatalSec/DaliVM
一个基于 Python 的 Dalvik 虚拟机模拟器,可在无需 Android 运行时环境的情况下对 APK 进行静态分析和字符串解密。
Stars: 85 | Forks: 7
# Dalvik 字节码模拟器
一个基于 Python 的 Dalvik VM 模拟器,专为 Android 应用的**静态分析和字符串解密**而设计。在无需完整 Android 运行时环境的情况下执行 APK 文件中的目标方法。
## 功能概览
| 类别 | 功能 |
|----------|--------------|
| **字节码执行** | 127+ Dalvik opcodes,包括算术运算、控制流、数组、字段和方法调用 |
| **Multi-DEX 支持** | 自动加载并索引 APK 中的所有 `classes*.dex` 文件 |
| **静态分析** | 后向数据流追踪和前向查找以进行参数解析 |
| **Android API 模拟** | Context、PackageManager、Signature、Reflection 和系统服务 |
| **Java 标准库** | String、StringBuilder、Integer、Math、Arrays、List/Iterator 钩子 |
| **灵活使用** | 可作为 CLI 工具使用或作为 Python 库导入 |
## 概述
该模拟器专注于**目标方法执行** —— 给定一个 APK 和一个目标方法签名,它会:
1. 识别对该方法的所有调用点
2. 解析每个调用点的参数(静态解析或通过部分执行)
3. 使用这些参数执行目标方法
4. 返回结果
这对于**解密 Android 恶意软件/应用中的混淆字符串**特别有用。
## 架构
```
┌─────────────────────────────────────────────────────────────────┐
│ emulate.py │
│ - Entry point, argument parsing, orchestration │
└───────────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ DexParser │ │ DependencyAnalyzer │ │ ClassLoader │
│ - Multi-DEX │ │ - Find call sites │ │ - Method │
│ - Strings │ │ - Resolve args │ │ resolution │
│ - Methods │ │ - Forward lookup │ │ - │
└───────────────┘ └─────────────────────┘ └──────────────┘
│
▼
┌─────────────────────┐
│ DalvikVM │
│ - Registers │
│ - PC management │
│ - Opcode dispatch │
└──────────┬──────────┘
│
┌──────────┬────────────┼────────────┬──────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐
│ const │ │ array │ │ control │ │ field │ │ invoke │
│ .py │ │ .py │ │ .py │ │ .py │ │ .py │
└────────┘ └────────┘ └──────────┘ └────────┘ └─────────┘
Opcode Handlers
```
## 快速开始
### 安装
```
pip install -r requirements.txt
```
### 基本 CLI 用法
```
# 模拟特定方法
python emulate.py app.apk "Lcom/example/Decryptor;->decrypt"
# 带详细输出
python emulate.py app.apk "Lcom/example/Decryptor;->decrypt" -v
# 带调试模式(详细执行跟踪)
python emulate.py app.apk "Lcom/example/Decryptor;->decrypt" --debug
# 限制结果
python emulate.py app.apk "Lcom/example/Decryptor;->decrypt" --limit 10
```
### 方法签名格式
```
LClassName;->methodName(ParameterTypes)ReturnType
Examples:
- LForwardLookupTests;->testFilledArray5
- Lutil/Crypto;->decrypt(Ljava/lang/String;)Ljava/lang/String;
- LMyClass;->compute(II)I
```
## 详细功能
### 1. 已实现的 Opcodes (总计 127+)
#### Move 操作 (0x01-0x0d)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x01 | move | 在寄存器之间移动值 |
| 0x02 | move/from16 | 从 16 位寄存器移动 |
| 0x03 | move/16 | 使用 16 位地址移动 |
| 0x04-0x06 | move-wide/* | 移动 64 位值 |
| 0x07-0x09 | move-object/* | 移动对象引用 |
| 0x0a | move-result | 移动方法返回值 |
| 0x0b | move-result-wide | 移动 64 位返回值 |
| 0x0c | move-result-object | 移动对象返回值 |
| 0x0d | move-exception | 移动异常对象 |
#### Return 操作 (0x0e-0x11)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x0e | return-void | 从 void 方法返回 |
| 0x0f | return | 返回 32 位值 |
| 0x10 | return-wide | 返回 64 位值 |
| 0x11 | return-object | 返回对象引用 |
#### Const 操作 (0x12-0x1c)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x12 | const/4 | 4 位有符号常量 |
| 0x13 | const/16 | 16 位有符号常量 |
| 0x14 | const | 32 位常量 |
| 0x15 | const/high16 | 32 位的高 16 位 |
| 0x16-0x19 | const-wide/* | 64 位常量 |
| 0x1a-0x1b | const-string | 从常量池加载字符串 |
| 0x1c | const-class | 加载类引用 |
#### Object 操作 (0x1d-0x27)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x1d | monitor-enter | 进入同步块 (no-op) |
| 0x1e | monitor-exit | 退出同步块 (no-op) |
| 0x1f | check-cast | 类型转换检查 |
| 0x20 | instance-of | 类型测试 |
| 0x21 | array-length | 获取数组长度 |
| 0x22 | new-instance | 创建新对象 |
| 0x23 | new-array | 创建新数组 |
| 0x24 | filled-new-array | 创建带值的数组 |
| 0x25 | filled-new-array/range | 创建数组 (范围) |
| 0x26 | fill-array-data | 从 payload 填充数组 |
| 0x27 | throw | 抛出异常 |
#### 控制流 (0x28-0x3d)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x28-0x2a | goto/* | 无条件跳转 |
| 0x2b | packed-switch | 密集 switch 语句 |
| 0x2c | sparse-switch | 稀疏 switch 语句 |
| 0x2d-0x31 | cmp* | 比较操作 (float, double, long) |
| 0x32-0x37 | if-* | 双寄存器条件判断 (eq, ne, lt, ge, gt, le) |
| 0x38-0x3d | if-*z | 零比较条件判断 |
#### Array 操作 (0x44-0x51)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x44-0x4a | aget* | 数组元素读取 (int, wide, object, boolean, byte, char, short) |
| 0x4b-0x51 | aput* | 数组元素写入 (所有类型) |
#### Field 操作 (0x52-0x6d)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x52-0x58 | iget* | 实例字段读取 |
| 0x59-0x5f | iput* | 实例字段写入 |
| 0x60-0x66 | sget* | 静态字段读取 |
| 0x67-0x6d | sput* | 静态字段写入 |
#### Invoke 操作 (0x6e-0x78)
| Opcode | 名称 | 描述 |
|--------|------|-------------|
| 0x6e | invoke-virtual | 虚方法调用 |
| 0x6f | invoke-super | 父类方法调用 |
| 0x70 | invoke-direct | 直接方法调用 |
| 0x71 | invoke-static | 静态方法调用 |
| 0x72 | invoke-interface | 接口方法调用 |
| 0x74-0x78 | invoke-*/range | 用于 >5 个参数的范围变体 |
#### 算术操作 (0x7b-0xe2)
- **一元操作**: neg-int, not-int, neg-long, not-long, neg-float, neg-double
- **类型转换**: int-to-*, long-to-*, float-to-*, double-to-*, int-to-byte/char/short
- **整数算术**: add, sub, mul, div, rem, and, or, xor, shl, shr, ushr
- **长整数算术**: 针对 64 位整数的相同操作
- **Float/Double 算术**: add, sub, mul, div, rem
- **2 地址形式**: 以上所有操作的目标操作数 = 源操作数 1
- **字面量形式**: 带有 8 位和 16 位字面量操作数的操作
### 2. Android API 模拟
模拟器为 Android framework API 提供了全面的模拟 (mock):
#### Context & Package 管理
| API | 模拟行为 |
|-----|---------------|
| `Context.getPackageName()` | 返回配置的包名 |
| `Context.getPackageManager()` | 返回模拟的 PackageManager |
| `PackageManager.getPackageInfo()` | 返回带有签名的模拟 PackageInfo |
| `PackageManager.getInstalledPackages()` | 返回包含模拟包的列表 |
| `Signature.toByteArray()` | 返回配置的证书字节 |
| `Signature.toCharsString()` | 返回证书的十六进制字符串 |
| `Signature.hashCode()` | 返回一致的哈希值 |
#### Reflection 支持
| API | 模拟行为 |
|-----|---------------|
| `Class.forName()` | 返回模拟的 Class 对象 |
| `Class.getMethod()` | 返回模拟的 Method 对象 |
| `Class.getField()` | 返回模拟的 Field 对象 |
| `Method.invoke()` | 尝试执行或返回 null |
| `Field.get()` | 返回字段值或 null |
| `Throwable.getCause()` | 返回 null |
#### 静态字段
| 字段 | 值 |
|-------|-------|
| `Build.VERSION.SDK_INT` | 可配置 (默认: 33) |
| `Boolean.TRUE/FALSE` | 包装后的 Boolean 对象 |
| `Integer.TYPE`, `Long.TYPE` 等 | 基本类型描述符 |
### 3. Java 标准库钩子
常用 Java 方法的内置实现:
#### String 操作
| 方法 | 实现 |
|--------|----------------|
| `String.length()` | 返回实际长度 |
| `String.charAt(i)` | 返回索引处的字符 |
| `String.toCharArray()` | 返回 char 数组 |
| `String.getBytes()` | 返回 UTF-16 LE 编码的字节 |
| `String.intern()` | 返回相同的字符串 |
| `String.valueOf(*)` | 将任何类型转换为 String |
#### StringBuilder
| 方法 | 实现 |
|--------|----------------|
| `StringBuilder.()` | 初始化空缓冲区 |
| `StringBuilder.append(*)` | 追加任何类型 |
| `StringBuilder.toString()` | 返回构建的字符串 |
#### 数值操作
| 方法 | 实现 |
|--------|----------------|
| `Integer.parseInt()` | 将字符串解析为 int |
| `Integer.valueOf()` | 将 int 包装为 Integer |
| `Long.parseLong()` | 将字符串解析为 long |
| `Boolean.valueOf()` | 将 boolean 包装为 Boolean |
| `*.intValue()`, `*.booleanValue()` | 拆箱包装类型 |
#### Math 操作
| 方法 | 实现 |
|--------|----------------|
| `Math.abs()` | 绝对值 |
| `Math.max()` | 两个值中的最大值 |
| `Math.min()` | 两个值中的最小值 |
#### 数组与集合
| 方法 | 实现 |
|--------|----------------|
| `Arrays.copyOf()` | 复制数组并设置新大小 |
| `System.arraycopy()` | 复制数组区域 |
| `List.size()` | 返回列表大小 |
| `List.get(i)` | 返回索引处的元素 |
| `List.iterator()` | 返回迭代器 |
| `Iterator.hasNext()` / `next()` | 迭代支持 |
| `Object.clone()` | 克隆数组 |
#### 工具类
| 方法 | 实现 |
|--------|----------------|
| `TextUtils.isEmpty()` | 检查是否为 null/空 CharSequence |
| `PrintStream.println()` | 打印到 stdout (可静默) |
### 4. 静态分析功能
#### 后向数据流分析
- 从 invoke 指令向后追踪,以查找参数寄存器获取其值的位置
- 处理 move 链、const 值、静态字段读取和方法结果
- 报告未解析的参数以回退到部分执行
#### 前向查找分析
当后向分析找到分配指令时,前向查找会扫描初始化操作:
| 模式 | 前向查找 |
|---------|----------------|
| `new-instance` | 查找 `invoke-direct ` 构造函数调用 |
| `new-array` | 查找 `fill-array-data` 填充操作 |
| 对象设置 | 捕获 `iput` 字段赋值 |
#### 依赖分析
- 分析方法字节码以查找访问的静态字段
- 识别需要 `` 初始化的类
- 追踪调用的方法以进行递归分析
### 5. Multi-DEX 支持
- 自动检测并加载 APK 中的所有 `classes*.dex` 文件
- 跨所有 DEX 文件构建统一索引
- 透明地解析跨 DEX 方法调用
- 正确处理 Unicode/MUTF-8 编码的方法名称
### 6. 类初始化
- 在需要时运行 `` 静态初始化器
- 从类定义加载静态字段值
- 跟踪已初始化的类以避免重复初始化
- 支持跨类静态字段解析
## 配置
通过 `dalvik_vm/mocks/config.py` 自定义模拟行为:
```
from dalvik_vm.mocks import mock_config
# 设置包标识
mock_config.package_name = "com.target.app"
# 设置 SDK 版本
mock_config.sdk_int = 33 # Android 13
# 设置签名证书(用于防篡改检查)
with open("original_cert.der", "rb") as f:
mock_config.signature_bytes = f.read()
```
## 文件结构
```
dalvik-emulator/
├── emulate.py # Main entry point & orchestration
├── cli.py # CLI wrapper
├── dalvik_vm/
│ ├── vm.py # DalvikVM class
│ ├── types.py # RegisterValue, DalvikObject, DalvikArray
│ ├── memory.py # StaticFieldStore singleton
│ ├── class_loader.py # Method resolution and execution
│ ├── dependency_analyzer.py # Call site finding, arg resolution
│ ├── static_analysis.py # Backward register tracing
│ ├── forward_lookup.py # Forward initialization patterns
│ ├── dex_parser.py # Multi-DEX file parsing
│ ├── android_mocks.py # Re-exports from mocks/
│ ├── mocks/
│ │ ├── config.py # Mock configuration
│ │ ├── factories.py # Mock object creation
│ │ ├── dispatch.py # Hook registry
│ │ ├── context_hooks.py # Context/PackageManager hooks
│ │ ├── reflection_hooks.py # Reflection support
│ │ └── utility_hooks.py # Utility hooks
│ └── opcodes/
│ ├── __init__.py # Dispatch table (127+ opcodes)
│ ├── const.py # Constant loading
│ ├── move.py # Register moves
│ ├── control.py # Branches, switches
│ ├── array.py # Array operations
│ ├── field.py # Instance/static field access
│ ├── invoke.py # Method invocation & hooks
│ ├── arithmetic.py # Math operations
│ ├── return_.py # Return instructions
│ └── objects.py # Object creation, type checks
├── tests/ # Test suite
│ ├── test_opcodes.py # Opcode unit tests
│ ├── test_android_mocks.py # Mock tests
│ └── test_hooks.py # Hook tests
```
## 测试
运行测试用例:
```
# 运行单元测试
python -m pytest tests/
# 测试 filled-new-array
python emulate.py forwardtest.apk "LForwardLookupTests;->testFilledArray5"
# 测试 switch 语句
python emulate.py forwardtest.apk "LForwardLookupTests;->testSwitchInit"
# 测试静态字段
python emulate.py forwardtest.apk "LForwardLookupTests;->testStaticArrayInit"
```
## 扩展性
请参阅 [DEVGUIDE.md](DEVGUIDE.md) 获取详细说明,内容涵盖:
- 添加新的 Android API 模拟
- 重写或添加 opcode 处理程序
- 自定义静态分析模式
- 扩展前向查找检测
## 限制
1. **无完整 Android framework** - 仅支持被模拟的 API
2. **无异常处理** - 异常未完全实现
3. **无线程** - 仅支持单线程执行
4. **无原生方法** - 不支持 JNI 调用
5 **无动态类加载** - 不支持 `DexClassLoader`
6. **无反射执行** - Reflection 被模拟而非执行
## 环境要求
- Python 3.8+
- 完整列表请参见 `requirements.txt`
## 许可证
GPL v3 许可证
标签:Android 安全, APK 分析, Dalvik 虚拟机, DAST, DEX 文件, Python 库, 云安全监控, 云资产清单, 代码脱壳, 反混淆, 字符串解密, 字节码模拟, 恶意软件分析, 无运行时分析, 目录枚举, 移动安全, 自动化安全工具, 逆向工具, 逆向工程, 静态分析