Skip to content

Commit

Permalink
Major Change (#4)
Browse files Browse the repository at this point in the history
* Bump Min. python version to `3.10`
* Add New Instructions
* Implement tests
* Refactor

The code now follows a more modular structure, with improved function definitions and better separation of concerns.

Fixed several linter (flake8, pylint) warnings

Shift to `argparse` for better command-line arguments parsing

Improved function for fetching the path to the `grammar.yaml` file.
  • Loading branch information
AbhiTheModder authored Jan 2, 2025
1 parent 4f7520a commit db5b9d7
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 159 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python package

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings.
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=180 --statistics
- name: Test with pytest
run: |
pytest
16 changes: 5 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@ build-backend = "setuptools.build_meta"

[project]
name = "smalig"
version = "0.1.6"
version = "0.1.7"
description = "Smali ByteCode info (grammar) fetch tool written in Python"
authors = [
{ name = "AbhiTheModder", email = "[email protected]" },
]
license = {file = "LICENSE"}
authors = [{ name = "AbhiTheModder", email = "[email protected]" }]
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.10"
keywords = ["smali", "grammar", "parser", "android", "reverse-engineering"]
classifiers = [
"Development Status :: 4 - Beta",
"Natural Language :: English",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand All @@ -29,10 +26,7 @@ classifiers = [
"Operating System :: OS Independent",
]

dependencies = [
"PyYAML",
"jsbeautifier",
]
dependencies = ["PyYAML", "jsbeautifier"]

[project.urls]
homepage = "https://github.com/RevEngiSquad/smalig"
Expand Down
6 changes: 3 additions & 3 deletions smalig/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .utils import YamlReader, InstructionFetch, cls
from .cli.app import main, app, help
from .utils import YamlReader, InstructionFetch, cls, grammar_yaml
from .cli.app import main

__all__ = ["YamlReader", "InstructionFetch", "main", "app", "help", "cls"]
__all__ = ["YamlReader", "InstructionFetch", "main", "cls", "grammar_yaml"]
59 changes: 2 additions & 57 deletions smalig/__main__.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,4 @@
import sys
from smalig import app, help, cls
from smalig import main

if __name__ == "__main__":
args = sys.argv[1:]

if "-h" in args or "--help" in args:
help()
sys.exit(0)

if "-m" in args:
exact_match = False
else:
exact_match = True

if "-o" in args:
try:
output_file = args[args.index("-o") + 1]
except IndexError:
output_file = None
else:
output_file = None

try:
if "-f" in args:
try:
file_path = args[args.index("-f") + 1]
except IndexError:
file_path = None
else:
file_path = input(f"Enter the path to the file: ")

if "-t" in args:
try:
target = args[args.index("-t") + 1]
except IndexError:
target = ""
else:
target = input(f"Enter Query: ")
cls()
except KeyboardInterrupt:
print("\nExiting...")
sys.exit(0)

if "-j" in args:
json = True
else:
if output_file and output_file.endswith(".json"):
json = True
else:
json = False

if target == "":
raise Exception("Query is empty")

if file_path == "":
raise Exception("File path is empty")

app(file_path=file_path, target=target, json=json, out=output_file, exact_match=exact_match)
main()
147 changes: 69 additions & 78 deletions smalig/cli/app.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
import sys
import argparse
import json as js
import jsbeautifier
import importlib.resources
import textwrap

from smalig import YamlReader, InstructionFetch, cls
from smalig import YamlReader, InstructionFetch, cls, grammar_yaml


def help() -> None:
help_message = """
smalig: Smali ByteCode info (grammar) fetch tool
Usage: smalig [-t TARGET [-j] [-o OUTPUT_FILE]
Options:
-t TARGET Specify the Smali instruction to fetch. If omitted,
prompts the user for input.
-j Output the result as JSON. If -o is also specified and the
OUTPUT_FILE ends in '.json', this flag is automatically set.
-o OUTPUT_FILE Write the output to the specified file. If omitted, prints to console.
Examples:
smalig -t "const-string" # Fetch information for the 'const-string' instruction.
smalig -t "invoke-virtual" -j -o output.json # Fetch and save as JSON
smalig -o my_output.txt # Prompts for instruction then saves to my_output.txt
If no target is specified using -t, the tool will prompt for input.
If no -o flag is used, the output goes to stdout. If a file is specified without a .json extension, plain text output is generated.
"""
print(textwrap.dedent(help_message))
EXAMPLES = """
examples:
smalig # Prompts for instruction then fetch it's information.
smalig -m # Prompts for instruction then fetch it's information with fuzzy match.
smalig -t "move" # Fetch information for the 'move' instruction.
smalig -t "move" -j # Output as JSON
smalig -t "invoke-virtual" -j -o output.json # Fetch and save as JSON
smalig -o my_output.txt # Prompts for instruction then saves to my_output.txt
smalig -t "move" -m # Fuzzy match
smalig -t "move" -o my_output.json # Save as JSON
smalig -t "move" -o my_output.txt # Save as plain text
"""


def app(file_path, target, json, out, exact_match) -> None:
Expand Down Expand Up @@ -74,60 +60,65 @@ def app(file_path, target, json, out, exact_match) -> None:
return


def main() -> None:
"""
Main function
"""
args = sys.argv[1:]

if "-h" in args or "--help" in args:
help()
return

if "-m" in args:
exact_match = False
else:
exact_match = True

if "-o" in args:
try:
output_file = args[args.index("-o") + 1]
except IndexError:
output_file = None
def parse_args():
parser = argparse.ArgumentParser(
prog="smalig",
description="Smali ByteCode info (grammar) fetch tool",
epilog=EXAMPLES,
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument("-m", action="store_true", help="Enable fuzzy match")
parser.add_argument(
"-o",
metavar="OUTPUT_FILE",
help="Specify output file. If omitted, prints to console.",
)
parser.add_argument(
"-t",
metavar="TARGET",
help="Specify the Smali instruction to fetch. If omitted, prompts the user for input.",
)
parser.add_argument(
"-j",
action="store_true",
help="Enable JSON output. If omitted and OUTPUT_FILE ends in '.json', this flag is automatically set.",
)
return parser.parse_args()


def get_target(args):
if args.t:
return args.t
else:
output_file = None
target = input("Search instruction: ")
cls()
return target

with importlib.resources.path("smalig", "grammar.yaml") as file_path:
file_path = str(file_path)

if "-t" in args:
try:
target = args[args.index("-t") + 1]
except IndexError:
target = ""
else:
try:
target = input(f"Enter Query: ")
cls()
except KeyboardInterrupt:
print("\nExiting...")
return

if "-j" in args:
json = True
def get_json(args, output_file):
if args.j:
return True
elif output_file and output_file.endswith(".json"):
return True
else:
if output_file and output_file.endswith(".json"):
json = True
else:
json = False

if target == "":
raise Exception("Query is empty")

if file_path == "":
raise Exception("File path is empty")

app(file_path=file_path, target=target, json=json, out=output_file, exact_match=exact_match)
return False


def main():
args = parse_args()
file_path = grammar_yaml()
target = get_target(args)
if not target:
exit("Query is empty!")
json_output = get_json(args, args.o)

app(
file_path=file_path,
target=target,
json=json_output,
out=args.o,
exact_match=not args.m,
)


if __name__ == "__main__":
Expand Down
48 changes: 48 additions & 0 deletions smalig/grammar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,54 @@
example: "1500 2041 - const/high16 v0, 0x41200000 (#float 10.0)"
example_disc: "Moves the floating literal value 0x41200000(10.0) into v0. The 16 bit literal in the instruction carries the top 16 bits of the floating point number."

- opcode: "16"
name: "const-wide/16"
format: "AA|op BBBB"
format_id: "21s"
syntax: "const-wide/16 vAA, #+BBBB"
args_info: "A: destination register (8 bits), B: signed int (16 bits)"
short_desc: "Move the given literal value (sign-extended to 64 bits) into the specified register-pair."
long_desc: "Moves the literal value BBBB into register-pair (vAA, vAA+1), expanding the integer constant into a long constant. BBBB is sign-extended to 64 bits. The value of BBBB is in the range -32768 to 32767 (-0x8000 to 0x7FFF)."
note: ""
example: "1600 0A00 - const-wide/16 v0, 0xa (#long 10.0)"
example_disc: "Moves the long literal value 0xa(10) into (v0,v1) register-pair."

- opcode: "17"
name: "const-wide/32"
format: "AA|op BBBBlo"
format_id: "31i"
syntax: "const-wide/32 vAA, #+BBBBBBBB"
args_info: "A: destination register (8 bits), B: signed int (32 bits)"
short_desc: "Move the given literal value (sign-extended to 64 bits) into the specified register-pair."
long_desc: "Moves the literal value BBBBBBBB into register-pair (vAA, vAA+1), expanding the integer constant into a long constant. BBBBBBBB is sign-extended to 64 bits. The value of BBBBBBBB is in the range -2147483648 to 2147483647 (-0x80000000 to 0x7FFFFFFF)."
note: ""
example: "1702 4E61 BC00 - const-wide/32 v2, 0x00bc614e"
example_disc: "Moves the long literal value 0x00bc614e into (v2,v3) register-pair."

- opcode: "18"
name: "const-wide"
format: "AA|op BBBBlo BBBB BBBB BBBBhi"
format_id: "51l"
syntax: "const-wide vAA, #+BBBBBBBBBBBBBBBB"
args_info: "A: destination register (8 bits), B: signed int (64 bits)"
short_desc: "Move the given literal value into the specified register-pair."
long_desc: "Moves the literal value constant BBBBBBBBBBBBBBBB into register-pair (vAA, vAA+1). The value of BBBBBBBBBBBBBBBB is in the range -9223372036854775808 to 9223372036854775807 (-0x8000000000000000 to 0x7FFFFFFFFFFFFFFF)."
note: ""
example: "1802 874b 6b5d 54dc 2b00- const-wide v2, 0x002bdc545d6b4b87 (#long 12345678901234567)"
example_disc: "Moves the long literal value 0x002bdc545d6b4b87 into (v2,v3) register-pair."

- opcode: "19"
name: "const-wide/high16"
format: "AA|op BBBB"
format_id: "21h"
syntax: "const-wide/high16 vAA, #+BBBB000000000000"
args_info: "A: destination register (8 bits), B: signed int (16 bits)"
short_desc: "Move the given literal value (right-zero-extended to 64 bits) into the specified register-pair."
long_desc: "Moves the literal value BBBB into register-pair (vAA, vAA+1). BBBB is right-zero-extended to 64 bits. The value of BBBB is in the range -32768 to 32767 (-0x8000 to 0x7FFF)."
note: "Generaly used to initialise double values."
example: "1900 2440 - const-wide/high16 v0, 0x402400000 (#double 10.0)"
example_disc: "Moves the double literal value 10.0 into (v0,v1) register-pair."

- opcode: "28"
name: "goto"
format: "AA|op"
Expand Down
Loading

0 comments on commit db5b9d7

Please sign in to comment.