Skip to main content

Orchestrate Android reverse engineering tools into a unified security analysis pipeline

Project description

batuta

Batuta is a Brazilian Portuguese word that means "baton" in English. Like a conductor's baton that directs an orchestra, batuta aims to be the main tool to orchestrates Android reverse engineering.

batuta is a Python CLI for static Android application analysis — designed for penetration testers, bug bounty hunters, and malware analysts. It wraps battle-tested RE tools (apktool, jadx, adb, APKEditor) behind a clean, composable interface.


Features

  • APK pulling — pull APKs by package name, app name, or filter pattern
  • Split APK support — automatically detects split packages, pulls every part, and merges them via batuta apk merge or --auto-merge
  • Decompilation — run jadx and/or apktool via batuta apk decompile or batuta apk pull --decompile
  • APK patching — rebuild, align, sign, and optionally install via batuta apk patch
  • Framework detection — detect cross-platform frameworks (Flutter, React Native, Xamarin, Cordova, Unity) via batuta analyze framework
  • Interactive selection — choose from multiple matches when searching (prompted automatically)
  • Scriptable by design — every command supports --json output for piping into jq, grep, or custom tooling
  • Library-first architecture — core logic is importable independently of the CLI

Requirements

Python

  • Python >= 3.14

External Tools

These must be installed and available on your PATH:

Tool Purpose Install
adb Android Debug Bridge — device communication Android SDK Platform Tools
apktool APK decoding, smali disassembly Apktool Webiste
jadx Java/Kotlin decompilation Jadx GitHub Repository
APKEditor Split APK merging APKEditor Repository

batuta checks for required tools at command entry and reports clear installation instructions when missing.

Configuring APKEditor

APKEditor ships as a JAR file. Batuta resolves it in this order:

  1. APKEDITOR_JAR environment variable (points to the .jar or its parent directory)
  2. ~/.batuta/config.jsonapkeditor_path
  3. Executable wrapper called APKEditor somewhere on PATH

Examples:

# 1. Environment variable (temporary for current shell)
export APKEDITOR_JAR="$HOME/tools/APKEditor/APKEditor.jar"

# 2. Config file (~/.batuta/config.json)
{
  "apkeditor_path": "~/tools/APKEditor/APKEditor.jar"
}

# 3. Wrapper script placed on PATH (e.g., /usr/local/bin/APKEditor)
#!/bin/bash
exec java -jar "$HOME/tools/APKEditor/APKEditor.jar" "$@"

You can point apkeditor_path to either the JAR file itself or the directory containing APKEditor.jar. Batuta keeps the pulled split directory intact after merging, regardless of the resolution method you use.


Installation

Install from source with uv:

git clone https://github.com/luca-regne/batuta
cd batuta
uv sync

Or with pip:

pip install -e .

Quick Start

# List connected devices
batuta device list

# Search for packages by name
batuta apk search google

# Get detailed info about a package
batuta apk info com.example.app

# Pull an APK (supports partial names and filters)
batuta apk pull youtube

# Pull with interactive selection when multiple matches (prompted automatically)

# Pull to a specific directory
batuta apk pull com.example.app --output ./apks/

# Pull and immediately decompile
batuta apk pull com.example.app --decompile

# Standalone decompile from local APK
batuta apk decompile ./apks/com.example.app.apk --java-only

# Merge a split APK directory (keeps original files)
batuta apk merge ./apks/com.example.app --output ./apks/com.example.app.merged.apk

# Detect cross-platform frameworks
batuta analyze framework ./apks/com.example.app.apk

# Framework detection with JSON output
batuta analyze framework ./apks/com.example.app.apk --json

Command Reference

batuta
├── analyze
│   └── framework <apk>            Detect cross-platform frameworks in APK
│
├── device
│   ├── list                       List connected ADB devices
│   └── shell [COMMAND...]         Open ADB shell (or run command)
│
└── apk
    ├── list                       List installed packages
    ├── search <query>             Search packages by name or filter
    ├── info <query>               Show detailed package information
    ├── pull <query>               Pull APK from connected device (optional decompile)
    ├── merge <dir>                Merge split APK folder into a single APK (keeps folder)
    ├── patch <apktool-dir>        Build/align/sign APK from apktool output
    └── decompile <apk>            Decompile APK to Java and/or smali

Common Options

Option Description
--device, -d Target specific device by ID
--json, -j Output as JSON for scripting
--system, -s Include system packages in search
--decompile (pull) Decompile after pulling (Java + smali)
--auto-merge (pull) Merge split APK folders via APKEditor
--java-only (pull/decompile) Limit to Java (jadx) output
--smali-only (pull/decompile) Limit to smali/resources (apktool)

Split APK pulls always save the original directory of base/split parts. Use --auto-merge for immediate merging (the folder stays untouched) or run batuta apk merge <dir> later. When --decompile is supplied, splits are merged automatically so jadx/apktool can run without extra steps.

Examples

# JSON output for scripting
batuta device list --json | jq '.[0].id'
batuta apk search facebook --json | jq '.[].package_name'

# Target specific device
batuta apk pull com.example.app --device RX8WC00D7JE

# Include system packages
batuta apk list --system --filter android

Library Usage

The core logic is importable independently of the CLI:

from batuta.core.adb import ADBWrapper

# List devices
adb = ADBWrapper()
devices = adb.list_devices()
for device in devices:
    print(f"{device.id}: {device.model} ({device.state})")

# Search packages
packages = adb.search_packages("google")
for pkg in packages:
    print(f"{pkg.package_name} v{pkg.version_name}")

# Pull an APK
result = adb.pull_apk("com.example.app", output_dir=Path("./apks"))
print(f"Pulled to: {result.local_path}")

Architecture

src/batuta/
├── cli/          # Typer commands — argument parsing and rich output only
├── core/         # Business logic — importable as a library
├── models/       # Pydantic v2 data models
├── utils/        # Shared utilities: deps checker, output helpers, process wrapper
└── exceptions.py # Typed exception hierarchy

Key architectural constraints:

  • cli/ never contains business logic — only calls into core/
  • All subprocess invocations go through utils/process.py
  • All external tool requirements are checked at command entry via utils/deps.py

Development

# Install with dev dependencies
uv sync --group dev

# Lint
ruff check src/batuta/

# Auto-fix lint issues
ruff check src/batuta/ --fix

# Type check
mypy src/batuta/

# Run the CLI
uv run batuta --help

License

MIT

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

batuta-0.1.0b1.tar.gz (51.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

batuta-0.1.0b1-py3-none-any.whl (58.1 kB view details)

Uploaded Python 3

File details

Details for the file batuta-0.1.0b1.tar.gz.

File metadata

  • Download URL: batuta-0.1.0b1.tar.gz
  • Upload date:
  • Size: 51.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for batuta-0.1.0b1.tar.gz
Algorithm Hash digest
SHA256 2471a5f7820cc93d090b79558def2c7d4b0c7de7ac1af123261687fb3166279a
MD5 1b4c64d784074de2f3c3d3669d9c7abb
BLAKE2b-256 0e75654c721c02fcbfc0a3618d37cf79c58ade4344a81fc90cba5b736616fc07

See more details on using hashes here.

File details

Details for the file batuta-0.1.0b1-py3-none-any.whl.

File metadata

  • Download URL: batuta-0.1.0b1-py3-none-any.whl
  • Upload date:
  • Size: 58.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for batuta-0.1.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 6071f668ec84256d6decb0ec9ef007f6fa0b0086f8554c04b3f799c566f39a71
MD5 3675ff8df2e5547a1f956e9c8134a198
BLAKE2b-256 3f27830cdd65f4d63c411cb7fa11e0c588a6558658a2f0dabfc0a922082cd4a8

See more details on using hashes here.

Supported by

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