Skip to main content

Smali Visitor-API and Smali emulator

Project description

PySmali

python Status Platform Build and Deploy Sphinx Documentation PyPI

The main functionalities of this repository cover creating and parsing Smali files with Python3 as well as interpret Smali source code files. There is also an interactive interpreter provided that acts as a Python-CLI.

Contributors

TheZ3ro serenees metalcorpe

Installation

By now, the only way to install the python module in this repository is by cloning it and running the following command:

$ cd ./pysmali && pip install .
# Or with pip
$ pip install pysmali

Usage

For a more detailed explanation of the Smali Visitor-API use the Github-Pages Docs.

Info: Make sure you are using pysmali>=0.2.0 as it introduces a user-friendly type system to mitigate possible issues from parsing type descriptors.

ISmali (Interactive Smali Interpreter)

As of version 0.1.2 the interactive interpreter can be used to execute Smali code directly:

$ ismali example.ssf
# or start interactive mode
$ ismali
>>> vars
{'p0': <SmaliObject@195f5c0da90>}

Some notes:

  • p0: This register always stores the root-instance where defined fields and methods will be stored.
  • vars: This command can be used to print all registers together with their values
  • L<Root>;: The name of the root-context class

The API documentation provides some usage examples and usage hints.

Parsing Smali-Files

The simplest way to parse code is to use a SmaliReader together with a visitor:

from smali import SmaliReader, ClassVisitor

code = """
.class public final Lcom/example/Hello;
.super Ljava/lang/Object;
# One line comment
.source "SourceFile" # EOL comment
"""

reader = SmaliReader()
reader.visit(code, ClassVisitor())

There are a few options to have in mind when parsing with a SmaliReader:

  • comments: To explicitly parse comments, set this variable to True (in constructor or directly)
  • snippet: To parse simple code snippets without a .class definition, use the 'snippet' variable (or within the constructor). Use this property only if you don't have a '.class' definition at the start of the source code
  • validate: Validates the parsed code
  • errors: With values "strict" or "ignore" this attribute will cause the reader to raise or ignore exceptions

Actually, the code above does nothing as the ClassVisitor class does not handle any notification by the reader. For instance, to print out the class name of a parsed code, the following implementation could be used:

from smali import SmaliReader, ClassVisitor, SVMType

class NamePrinterVisitor(ClassVisitor):
    def visit_class(self, name: str, access_flags: int) -> None:
        # The provided name is the type descriptor, so we have to
        # convert it:
        cls_type = SVMType(name)
        print('Class:', cls_type.pretty_name) # prints: com.example.Hello

reader = SmaliReader()
reader.visit(".class public final Lcom/example/Hello;", NamePrinterVisitor())

[!TIP] There is an example Smali file in this repository. If you want to print out all defined classes, you have to implement another method (based on the example above):

class NamePrinterVisitor(ClassVisitor):
  # ... method from above does not change
  def visit_inner_class(self, name: str, access_flags: int) -> ClassVisitor:
       cls_type = SVMType(name) # same as above
       print("Inner Class:", cls_type.pretty_name)
       return self

Writing Smali-Files

Writing is as simple as parsing files. To write the exact same document the has been parsed, the SmaliWriter class can be used as the visitor:

from smali import SmaliReader, SmaliWriter

reader = SmaliReader()
writer = SmaliWriter()

reader.visit(".class public final Lcom/example/Hello;", writer)
# The source code can be retrieved via a property
text = writer.code

To create own Smali files, the pre-defined SmaliWriter can be used again:

from smali import SmaliWriter, AccessType

writer = SmaliWriter()
# create the class definition
writer.visit_class("Lcom/example/Hello;", AccessType.PUBLIC + AccessType.FINAL)
writer.visit_super("Ljava/lang/Object;")

# create a field
field_writer = writer.visit_field("foo", AccessType.PRIVATE, "Ljava/lang/String")

# create the finished source code, BUT don't forget visit_end()
writer.visit_end()
text = writer.code

Importing classes and execute methods

As of version 0.1.2 you can import Smali files and execute defined methods:

from smali.bridge import SmaliVM, SmaliObject

vm = SmaliVM()
# Import class definition
with open('example.smali', 'r', encoding='utf-8') as fp:
    smali_class = vm.classloader.load_class(fp, init=False)
# Call <clinit> method
smali_class.clinit()

# Create a new instance of the imported class
instance = SmaliObject(smali_class)
# Call the object's constructor
instance.init()

# Execute the method 'toString'
toString = instance.smali_class.method("toString")
# The instance must be always the first element (on
# static methods this argument must be None)
value = toString(instance)
print(value)

License

Distributed under the GNU GPLv3. See LICENSE for more information.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pysmali-0.2.6.tar.gz (56.5 kB view details)

Uploaded Source

Built Distribution

pysmali-0.2.6-py3-none-any.whl (68.2 kB view details)

Uploaded Python 3

File details

Details for the file pysmali-0.2.6.tar.gz.

File metadata

  • Download URL: pysmali-0.2.6.tar.gz
  • Upload date:
  • Size: 56.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.7

File hashes

Hashes for pysmali-0.2.6.tar.gz
Algorithm Hash digest
SHA256 fcb722450628622354a3e41d5278fbdeb2c5cdf6c5ea4afbc0bc2c6755cf9766
MD5 aa3843501af04291a984ec4943a1ae95
BLAKE2b-256 da32aba75e81324c6685831690753449b1d63bb3a6e5282e89e8b7cdfd1e2537

See more details on using hashes here.

File details

Details for the file pysmali-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: pysmali-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 68.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.7

File hashes

Hashes for pysmali-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 01bba3c0ec12b2a2d9297304b5a1b0e7cd16b060774f245a7ec0adffd4eaba6b
MD5 88bcef1964395657043c7b0491e7ecb1
BLAKE2b-256 a07bd7b8b34b69ebb3669723927fa6bc28343949894a094a409eb81d9d3a73a9

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page