A Python-based Dalvik VM emulator designed for static analysis and string decryption in Android applications. Execute targeted methods within APK files without requiring a full Android runtime.
Category
Capabilities
Bytecode Execution
127+ Dalvik opcodes including arithmetic, control flow, arrays, fields, and method invocation
Multi-DEX Support
Automatically loads and indexes all classes*.dex files from APKs
Static Analysis
Backward data-flow tracing and forward lookup for argument resolution
Android API Mocking
Context, PackageManager, Signature, Reflection, and system services
Java Standard Library
String, StringBuilder, Integer, Math, Arrays, List/Iterator hooks
Flexible Usage
CLI tool or import as a Python library
This emulator focuses on targeted method execution - given an APK and a target method signature, it:
Identifies all call sites to that method
Resolves the arguments at each call site (statically or via partial execution)
Executes the target method with those arguments
Returns the results
This is particularly useful for decrypting obfuscated strings in Android malware/apps.
┌─────────────────────────────────────────────────────────────────┐
│ emulate.py │
│ - Entry point, argument parsing, orchestration │
└───────────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ DexParser │ │ DependencyAnalyzer │ │ ClassLoader │
│ - Multi-DEX │ │ - Find call sites │ │ - Method │
│ - Strings │ │ - Resolve args │ │ resolution │
│ - Methods │ │ - Forward lookup │ │ - <clinit> │
└───────────────┘ └─────────────────────┘ └──────────────┘
│
▼
┌─────────────────────┐
│ DalvikVM │
│ - Registers │
│ - PC management │
│ - Opcode dispatch │
└──────────┬──────────┘
│
┌──────────┬────────────┼────────────┬──────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐
│ const │ │ array │ │ control │ │ field │ │ invoke │
│ .py │ │ .py │ │ .py │ │ .py │ │ .py │
└────────┘ └────────┘ └──────────┘ └────────┘ └─────────┘
Opcode Handlers
pip install -r requirements.txt
# Emulate a specific method
python emulate.py app.apk " Lcom/example/Decryptor;->decrypt"
# With verbose output
python emulate.py app.apk " Lcom/example/Decryptor;->decrypt" -v
# With debug mode (detailed execution tracing)
python emulate.py app.apk " Lcom/example/Decryptor;->decrypt" --debug
# Limit results
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. Implemented Opcodes (127+ total)
Move Operations (0x01-0x0d)
Opcode
Name
Description
0x01
move
Move value between registers
0x02
move/from16
Move from 16-bit register
0x03
move/16
Move with 16-bit addresses
0x04-0x06
move-wide/*
Move 64-bit value
0x07-0x09
move-object/*
Move object reference
0x0a
move-result
Move method return value
0x0b
move-result-wide
Move 64-bit return value
0x0c
move-result-object
Move object return value
0x0d
move-exception
Move exception object
Return Operations (0x0e-0x11)
Opcode
Name
Description
0x0e
return-void
Return from void method
0x0f
return
Return 32-bit value
0x10
return-wide
Return 64-bit value
0x11
return-object
Return object reference
Const Operations (0x12-0x1c)
Opcode
Name
Description
0x12
const/4
4-bit signed constant
0x13
const/16
16-bit signed constant
0x14
const
32-bit constant
0x15
const/high16
High 16 bits of 32-bit
0x16-0x19
const-wide/*
64-bit constants
0x1a-0x1b
const-string
Load string from pool
0x1c
const-class
Load class reference
Object Operations (0x1d-0x27)
Opcode
Name
Description
0x1d
monitor-enter
Enter synchronized block (no-op)
0x1e
monitor-exit
Exit synchronized block (no-op)
0x1f
check-cast
Type cast check
0x20
instance-of
Type test
0x21
array-length
Get array length
0x22
new-instance
Create new object
0x23
new-array
Create new array
0x24
filled-new-array
Create array with values
0x25
filled-new-array/range
Create array (range)
0x26
fill-array-data
Fill array from payload
0x27
throw
Throw exception
Opcode
Name
Description
0x28-0x2a
goto/*
Unconditional branch
0x2b
packed-switch
Dense switch statement
0x2c
sparse-switch
Sparse switch statement
0x2d-0x31
cmp*
Compare operations (float, double, long)
0x32-0x37
if-*
Two-register conditionals (eq, ne, lt, ge, gt, le)
0x38-0x3d
if-*z
Zero-compare conditionals
Array Operations (0x44-0x51)
Opcode
Name
Description
0x44-0x4a
aget*
Array element read (int, wide, object, boolean, byte, char, short)
0x4b-0x51
aput*
Array element write (all types)
Field Operations (0x52-0x6d)
Opcode
Name
Description
0x52-0x58
iget*
Instance field read
0x59-0x5f
iput*
Instance field write
0x60-0x66
sget*
Static field read
0x67-0x6d
sput*
Static field write
Invoke Operations (0x6e-0x78)
Opcode
Name
Description
0x6e
invoke-virtual
Virtual method call
0x6f
invoke-super
Super method call
0x70
invoke-direct
Direct method call
0x71
invoke-static
Static method call
0x72
invoke-interface
Interface method call
0x74-0x78
invoke-*/range
Range variants for >5 arguments
Arithmetic Operations (0x7b-0xe2)
Unary ops : neg-int, not-int, neg-long, not-long, neg-float, neg-double
Type conversions : int-to-, long-to- , float-to-, double-to- , int-to-byte/char/short
Integer arithmetic : add, sub, mul, div, rem, and, or, xor, shl, shr, ushr
Long arithmetic : Same operations for 64-bit integers
Float/Double arithmetic : add, sub, mul, div, rem
2-address forms : All above with destination = source1
Literal forms : Operations with 8-bit and 16-bit literal operands
The emulator provides comprehensive mocks for Android framework APIs:
Context & Package Management
API
Mock Behavior
Context.getPackageName()
Returns configured package name
Context.getPackageManager()
Returns mock PackageManager
PackageManager.getPackageInfo()
Returns mock PackageInfo with signatures
PackageManager.getInstalledPackages()
Returns list with mock package
Signature.toByteArray()
Returns configured certificate bytes
Signature.toCharsString()
Returns hex string of certificate
Signature.hashCode()
Returns consistent hash
API
Mock Behavior
Class.forName()
Returns mock Class object
Class.getMethod()
Returns mock Method object
Class.getField()
Returns mock Field object
Method.invoke()
Attempts to execute or returns null
Field.get()
Returns field value or null
Throwable.getCause()
Returns null
Field
Value
Build.VERSION.SDK_INT
Configurable (default: 33)
Boolean.TRUE/FALSE
Wrapped Boolean objects
Integer.TYPE, Long.TYPE, etc.
Primitive type descriptors
3. Java Standard Library Hooks
Built-in implementations for common Java methods:
Method
Implementation
String.length()
Returns actual length
String.charAt(i)
Returns character at index
String.toCharArray()
Returns char array
String.getBytes()
Returns UTF-16 LE encoded bytes
String.intern()
Returns same string
String.valueOf(*)
Converts any type to String
Method
Implementation
StringBuilder.<init>()
Initializes empty buffer
StringBuilder.append(*)
Appends any type
StringBuilder.toString()
Returns built string
Method
Implementation
Integer.parseInt()
Parses string to int
Integer.valueOf()
Wraps int in Integer
Long.parseLong()
Parses string to long
Boolean.valueOf()
Wraps boolean in Boolean
*.intValue(), *.booleanValue()
Unwraps boxed types
Method
Implementation
Math.abs()
Absolute value
Math.max()
Maximum of two values
Math.min()
Minimum of two values
Method
Implementation
Arrays.copyOf()
Copy array with new size
System.arraycopy()
Copy array region
List.size()
Returns list size
List.get(i)
Returns element at index
List.iterator()
Returns iterator
Iterator.hasNext() / next()
Iteration support
Object.clone()
Clones arrays
Method
Implementation
TextUtils.isEmpty()
Checks for null/empty CharSequence
PrintStream.println()
Prints to stdout (can be silenced)
4. Static Analysis Features
Backward Data-Flow Analysis
Traces from invoke instructions backward to find where argument registers get their values
Handles move chains, const values, static field reads, and method results
Reports unresolved arguments for partial execution fallback
When backward analysis finds allocation instructions, forward lookup scans for initialization:
Pattern
Forward Lookup
new-instance
Finds invoke-direct <init> constructor call
new-array
Finds fill-array-data population
Object setup
Captures iput field assignments
Analyzes method bytecode to find static fields accessed
Identifies classes needing <clinit> initialization
Tracks methods called for recursive analysis
Automatically detects and loads all classes*.dex files from APKs
Builds unified index across all DEX files
Resolves cross-DEX method calls transparently
Handles Unicode/MUTF-8 encoded method names correctly
Runs <clinit> static initializers when needed
Loads static field values from class definitions
Tracks initialized classes to avoid re-initialization
Supports cross-class static field resolution
Customize mock behavior via dalvik_vm/mocks/config.py:
from dalvik_vm .mocks import mock_config
# Set package identity
mock_config .package_name = "com.target.app"
# Set SDK version
mock_config .sdk_int = 33 # Android 13
# Set signing certificate (for anti-tampering checks)
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
Run test cases:
# Run unit tests
python -m pytest tests/
# Test filled-new-array
python emulate.py forwardtest.apk " LForwardLookupTests;->testFilledArray5"
# Test switch statement
python emulate.py forwardtest.apk " LForwardLookupTests;->testSwitchInit"
# Test static fields
python emulate.py forwardtest.apk " LForwardLookupTests;->testStaticArrayInit"
See DEVGUIDE.md for detailed instructions on:
Adding new Android API mocks
Overriding or adding opcode handlers
Customizing static analysis patterns
Extending forward lookup detection
No full Android framework - Only mocked APIs are supported
No exception handling - Exceptions are not fully implemented
No threading - Single-threaded execution only
No native methods - JNI calls not supported
No dynamic class loading - DexClassLoader not supported
No reflection execution - Reflection is mocked, not executed
Python 3.8+
See requirements.txt for full list
GPL v3 License