PySmali is a Python library for parsing and unparsing smali files for programatic modification.
It is a line (not token) based parser. Its primary goal is for parsed files to maintain 100% equality with their original forms when reconstructed.
PySmali's main usage is for smali file patching. You are able to parse, search, extract, replace, and unparse blocks of a smali file.
Parsing is based on the ANTLR files found in the JesusFreke/smali repository.
Since this is a line, and not token, based parser, there are likely to be edge cases where PySmali fails to properly parse or unparse a file. There are currently 6,846 smali files that are used in the tests folder (tests/tests.tar.xz).
If you run into a smali file that does not parse or unparse properly, please submit a new issue with the complete smali file(s) attached as a zip or gz archive.
- Python 3.8 or newer
pip install smaliimport time
from smali import SmaliFile
from smali.statements import Statement
smali_file = SmaliFile.parse_file('/path/to/file.smali')
new_lines = Statement.parse_lines(f'''
# This file was modified by PySmali
# Modified: {time.ctime()}
''')
smali_file.root.extend(new_lines)
with open('/path/to/file.smali', 'w') as f:
f.write(str(smali_file))-
[UPCOMING] v0.4.0
- Complete parsing of body statements
-
[UPCOMING] v0.3.0
StatementandBlocksearching by method and field names- Simplified
StatementandBlockextraction and insertion
-
v0.2.5
- Removed all dependencies and reorganized utility code
-
v0.2.4
- Complete parsing and unparsing of non-body statements validated by current test suite
- The smali file is ingested on a line by line basis
- Each line is parsed into one or more
Statementinstances.super Ljava/lang/Object;would become a singleStatementinstancevalue = { LFormat31c; }would become 4Statementinstancesvalue,{,LFormat31c;,}
- Each
Statementinstance is subclassed based on its type- E.g.
FieldStatementorMethodStatement
- E.g.
- A
Statementcan have zero or moreStatementAttributesthat indicate its intent and format- E.g.
BLOCK_START,ASSIGNMENT_LHS, orNO_BREAK
- E.g.
- Multiple
Statementinstances can be joined into aBlockand nested where appropriate- A
Blockexample would be a smali method, comprised of beginning, body, and endStatementinstances
- A
- A
Statementparses its source line, split by whitespace - Parsing is done in two passes. This is due to the fact that the same line can be the start of a block, or a solo line depending on the existence of a matching
EndStatement.- The first pass builds a flat list of
Statementinstances from the input lines. - A
Statementthat can be either aBlockstart or a solo line is marked with theMAYBE_BLOCK_STARTattribute - If an
EndStatementis generated, and matches a previously markedStatement, the markedStatementis switched fromMAYBE_BLOCK_STARTtoBLOCK_START. - After the first pass, any remaining
Statementinstances that are still marked withMAYBE_BLOCK_STARTare switched toSINGLE_LINE, - The second pass iterates over the flat list of
Statementinstances and groups them intoBlockinstances and nesting when appropriate based on theSINGLE_LINEandBLOCK_STARTattributes.
- The first pass builds a flat list of
- Unparsing is done in a single pass
- Each
Statementstringifies itself using its own local information - The
SmaliFileinstance uses the attributes of eachStatementto stitch lines together and indent blocks where necessary
- Each
JesusFreke/smali by Ben Gruver
Licensed Under: Various Licenses
Smali files used as tests in the tests/tests.tar.xz archive have been obtained from the following projects:
- Android
- AndroidX
- FasterXML
- Java
- JavaX
- OkHttp
- Smali