1. Smali VM

Implementation of a simple Smali emulator named SmaliVM. It supports execution of small code snippets as well as the execution of whole class files.

Debugging can be done by providing a DebugHandler and enabling the debug-option wihtin the VM object. It is also possible to use a custom ClassLoader to load or define new classes.

As of version 0.1.1 it is possible to import Smali source code files into the provided SmaliVM. All classes are stored globally, so there can’t be two classes with the same type descriptor. To create a simple Smali emulator, just create a new SmaliVM instance:

from smali.bridge import SmaliVM

vm = SmaliVM()

Hint

If you want to create a custom import process, just create sub-class of ClassLoader and provide an instance of it in the constructor of the SmaliVM.

Next, classes are used as prototypes to create new objects of their type. The class creation and object initialization is rather simple:

 1from smali.bridge import SmaliClass, SmaliObject, SmaliVM
 2
 3# Let's assume, the class' source code is stored here
 4source = ...
 5
 6vm = SmaliVM()
 7# Load and define the class (don't call the <clinit> method)
 8my_class = vm.classloader.load_class(source, init=False)
 9
10# To manually initialize the class, call <clinit>
11my_class.clinit()
12
13# Instances can be created by providing the class object
14instance = SmaliObject(my_class)
15
16# Initialization must be done separately:
17instance.init(...)
18
19# Methods are stored in our class object (not in the
20# actual instance)
21toString = my_class.method("toString")
22
23# The first argument of instance method must be the
24# object itself (on static methods, just use None)
25value = toString(instance)
26
27# The returned value behaves like a string
28print("Returned value:", value)
class smali.bridge.vm.SmaliVM(class_loader: ClassLoader = None, executors: dict = None, use_strict: bool = False)

Basic implementation of a Smali emulator in Python.

__classes: dict[str, SmaliClass] = {}

All classes are stored in a dict

__frames: dict[int, Frame] = {}

Stores all execution frames mapped to their method object

call(method: SmaliMethod, instance, *args, **kwargs) object

Executes the given method in the given object instance.

Before the method will be executed, there is an input parameter check to validate all passed arguments. The required registers will be filled automatically.

Debugging is done via the DebugHandler that must be set globally in this object.

Parameters:
  • method (SmaliMethod) – the method to execute

  • instance (SmaliObject) – the smali object

Raises:
Returns:

the return value of the executed method

Return type:

object

classloader: ClassLoader

The class loader used to define classes.

debug_handler: DebugHandler

The debug handler to use.

executors: dict[str, Executor]

External executors used to operate on a single opcode.

get_class(name: str) SmaliClass

Searches for a class with the given name.

Parameters:

name (str) – the class name

Raises:

NoSuchClassError – if no class with the given name is defined

Returns:

the defined Smali class

Return type:

SmaliClass

new_class(_SmaliVM__class: SmaliClass)

Defines a new class that can be accessed globally.

Parameters:

cls (SmaliClass) – the class to be defined

new_frame(method: SmaliMethod, frame: Frame)

Creates a new method frame that will be mapped to the method’s signature-

Parameters:
  • method (SmaliMethod) – the target method

  • frame (Frame) – the execution frame

use_strict: bool = False

Tells the VM to throw exceptions on unkown opcodes.

class smali.bridge.vm.ClassLoader

Abstract base class for SmaliClassLoader

abstract define_class(source: bytes | str | IOBase) SmaliClass

Defines a new SmaliClass by parsing the given source file.

Parameters:

source (bytes | str) – the source code

Returns:

the parsed class definition

Return type:

SmaliClass

abstract load_class(source: str | bytes | IOBase, init=True, lookup_missing=False) SmaliClass

Parses the given source code and initializes the given class if enabled.

Parameters:
  • source (str | bytes | IOBase) – the source code

  • init (bool, optional) – whether <clinit> should be executed, defaults to True

  • lookup_missing (bool, optional) – whether missing classes should be searched before parsing can continue, defaults to False

Raises:
Returns:

the parsed class definition

Return type:

SmaliClass