Skip to content

dex2smali.py 是一个用于批量反编译 Android .dex 文件为 .smali 的 Python 脚本,底层调用 baksmali.jar 进行实际转换。(dex2smali.py is a lightweight Python script for batch decompiling Android .dex files into .smali format. It leverages baksmali.jar under the hood to perform the actual conversion.)

Notifications You must be signed in to change notification settings

CYRUS-STUDIO/dex2smali

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

smali 与 baksmali

smali 和 baksmali 是用于 Android 平台中 DEX 文件的汇编器和反汇编器,广泛应用于 Android 逆向分析与调试。

smali 和 baksmali 是一对工具,分别用于:

  • smali:将 smali 代码(Java汇编语言)编译成 DEX 文件。

  • baksmali:将 DEX 文件反汇编为 smali 代码。

开源地址:

Release版本下载地址:https://github.com/baksmali/smali/releases

使用 baksmali.jar 将 .dex 文件反汇编为 .smali

java -jar baksmali.jar disassemble "D:\Python\anti-app\app\douyin\dump_dex\base.apk.dex" -o smali
  • 输入 DEX 文件路径。

  • -o smali:指定输出目录,保存生成的 .smali 文件。

你也可以加上 --api 指定 Android API 级别:

java -jar baksmali.jar d "D:\Python\anti-app\app\douyin\dump_dex\base.apk.dex" -o smali --api 33

使用 smali.jar 将 .smali 汇编回 .dex 文件

java -jar smali.jar assemble smali -o new_classes.dex
  • smali:输入的 smali 文件目录。

  • -o new_classes.dex:输出的 DEX 文件名。

同样可以加上 --api 参数:

java -jar smali.jar assemble smali -o new_classes.dex --api 33

通过 python 脚本批量反汇编 dex

dex2smali.py 是一个用于批量反编译 Android .dex 文件为 .smali 的 Python 脚本,底层调用 baksmali.jar 进行实际转换。

支持如下功能:

  • 遍历指定目录下的所有 .dex 文件进行反编译

  • 自动排序 classes.dex, classes2.dex, ..., classesN.dex 确保按加载顺序反编译

  • 支持将多个 dex 反编译结果:合并到一个目录(默认),或分别输出(使用 --no-merge)

  • 默认输出到 <input_dir>/smali,可通过 --output 指定输出目录

代码实现如下:

import os
import re
import shutil
import subprocess
from pathlib import Path
from typing import List

# 当前脚本目录
SCRIPT_DIR = Path(__file__).resolve().parent
# 固定的 baksmali.jar 路径
BAKSMALI_JAR_PATH = SCRIPT_DIR / "baksmali.jar"


def merge_smali(temp_dir: Path, output_dir: Path):
    """
    将临时目录下的 smali 文件合并到输出目录中,遇到同名文件后者覆盖前者。
    """
    for root, _, files in os.walk(temp_dir):
        for file in files:
            if file.endswith(".smali"):
                rel_path = Path(root).relative_to(temp_dir) / file
                dest_path = output_dir / rel_path
                os.makedirs(dest_path.parent, exist_ok=True)
                shutil.copyfile(os.path.join(root, file), dest_path)


def sort_dex_paths(dex_paths: List[Path]) -> List[Path]:
    """
    对包含 classes.dex、classesN.dex 的 dex 文件路径进行排序,
    确保 classes2.dex 排在 classes10.dex 前面。

    :param dex_paths: 未排序的 dex 文件路径列表
    :return: 排序后的 dex 文件路径列表
    """

    def extract_index(dex_path: Path) -> int:
        name = dex_path.name
        if name == "classes.dex":
            return 1
        match = re.match(r"classes(\d+)\.dex$", name)
        if match:
            return int(match.group(1))
        return float('inf')

    return sorted(dex_paths, key=extract_index)


def dex_to_smali(input_dir: Path, output_dir: Path = None, merge: bool = True):
    """
    将指定目录下的所有 .dex 文件转换为 smali 文件。

    参数:
        input_dir (Path): 包含 .dex 文件的输入目录。
        output_dir (Path): smali 输出目录,默认 input_dir/smali。
        merge (bool): 是否合并 smali 文件,默认 True。
    """

    if not input_dir.exists() or not input_dir.is_dir():
        print(f"❌ 输入目录无效:{input_dir}")
        return

    if not BAKSMALI_JAR_PATH.exists():
        print(f"❌ 缺少 baksmali.jar,请将其放到当前脚本目录下:{BAKSMALI_JAR_PATH}")
        return

    if output_dir is None:
        output_dir = input_dir / "smali"

    os.makedirs(output_dir, exist_ok=True)

    dex_files = list(input_dir.rglob("*.dex"))
    sorted_dex_files = sort_dex_paths(dex_files)

    for dex_file in sorted_dex_files:
        print(f"🔧 正在反编译:{dex_file}")

        temp_smali_dir = output_dir if not merge else output_dir.parent / f".temp_smali_{dex_file.stem}"
        if merge and temp_smali_dir.exists():
            shutil.rmtree(temp_smali_dir)
        os.makedirs(temp_smali_dir, exist_ok=True)

        cmd = [
            "java", "-jar", str(BAKSMALI_JAR_PATH),
            "d",
            str(dex_file),
            "-o", str(temp_smali_dir)
        ]

        try:
            subprocess.run(cmd, check=True)
            if merge:
                merge_smali(temp_smali_dir, output_dir)
                print(f"✅ 成功合并 smali 到:{output_dir}")
            else:
                print(f"✅ 输出到目录:{temp_smali_dir}")
        except subprocess.CalledProcessError as e:
            print(f"❌ 反编译失败:{dex_file}\n{e}")
        finally:
            if merge:
                shutil.rmtree(temp_smali_dir, ignore_errors=True)


if __name__ == "__main__":
    r"""
    示例用法:

        # 默认合并所有 dex 的 smali 输出到 <input_dir>/smali
        python dex2smali.py D:\Path\To\dex_dir

        # 指定输出目录(仍合并)
        python dex2smali.py D:\Path\To\dex_dir --output D:\output\smali

        # 不合并,每个 dex 文件单独输出
        python dex2smali.py D:\Path\To\dex_dir --no-merge

        # 不合并 + 指定输出目录
        python dex2smali.py D:\Path\To\dex_dir --output D:\output\smali --no-merge
    """

    import argparse

    parser = argparse.ArgumentParser(description="批量将 DEX 文件转换为 Smali")
    parser.add_argument("input_dir", type=str, help="输入 DEX 文件目录")
    parser.add_argument("--output", type=str, help="输出 smali 目录(可选)")
    parser.add_argument("--no-merge", action="store_true", help="不合并 smali 输出,按 dex 文件分开")

    args = parser.parse_args()
    input_path = Path(args.input_dir).resolve()
    output_path = Path(args.output).resolve() if args.output else None

    dex_to_smali(input_path, output_path, merge=not args.no_merge)

执行脚本批量反汇编指定目录下的 dex 文件:

(anti-app) PS D:\Python\anti-app\dex2smali> python dex2smali.py D:\Python\anti-app\app\douyin\dex
🔧 正在反编译:D:\Python\anti-app\app\douyin\dex\base.apk.dex
✅ 成功合并 smali 到:D:\Python\anti-app\app\douyin\dex\smali
🔧 正在反编译:D:\Python\anti-app\app\douyin\dex\base.apk_classes2.dex
✅ 成功合并 smali 到:D:\Python\anti-app\app\douyin\dex\smali
🔧 正在反编译:D:\Python\anti-app\app\douyin\dex\base.apk_classes3.dex
✅ 成功合并 smali 到:D:\Python\anti-app\app\douyin\dex\smali

效果如下:

word/media/image1.png

使用说明

# 默认行为:反编译并合并输出到 <input_dir>/smali
python dex2smali.py D:\AndroidApp\dump_dex

# 指定输出目录(仍合并)
python dex2smali.py D:\AndroidApp\dump_dex --output D:\Output\smali

# 不合并输出,每个 dex 单独输出
python dex2smali.py D:\AndroidApp\dump_dex --no-merge

# 不合并 + 自定义输出目录
python dex2smali.py D:\AndroidApp\dump_dex --output D:\Output\smali --no-merge

输出目录结构示例:

  1. 合并模式(默认)
<output_dir>/smali/com/example/...
  1. 不合并模式(每个 dex 输出在独立目录)
<output_dir>/classes/com/example/...
<output_dir>/classes2/com/example/...

完整源码

开源地址:https://github.com/CYRUS-STUDIO/dex2smali

About

dex2smali.py 是一个用于批量反编译 Android .dex 文件为 .smali 的 Python 脚本,底层调用 baksmali.jar 进行实际转换。(dex2smali.py is a lightweight Python script for batch decompiling Android .dex files into .smali format. It leverages baksmali.jar under the hood to perform the actual conversion.)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages