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 库, 云安全监控, 云资产清单, 代码脱壳, 反混淆, 字符串解密, 字节码模拟, 恶意软件分析, 无运行时分析, 目录枚举, 移动安全, 自动化安全工具, 逆向工具, 逆向工程, 静态分析