amruth-sn/pyjadx

GitHub: amruth-sn/pyjadx

JADX 的 Python 绑定库,通过 JPype 在进程内将 Android APK/DEX 反编译为 Java 源码并提供类、方法、字段和交叉引用的编程访问接口。

Stars: 2 | Forks: 0

# pyjadx 通过 JPype 实现的 [JADX](https://github.com/skylot/jadx) Python 绑定。从 Python 将 Android APK 和 DEX 文件反编译为 Java 源代码。 ``` from pyjadx import Jadx with Jadx("app.apk") as jadx: for cls in jadx.classes: print(cls.full_name) print(cls.code) ``` ## 为什么选择 pyjadx? JADX 是领先的开源 Android 反编译器 —— 它能从 Dalvik 字节码生成干净的 Java/Kotlin 源代码,保留类层次结构、泛型、异常处理和注解。pyjadx 将其引入 Python,且无子进程开销:JADX 通过 JPype 在进程内运行。 ## 安装 ``` pip install pyjadx ``` **系统要求:** JDK 11+(JADX JARs 会在首次使用时自动下载)。 ## 快速开始 ``` from pyjadx import Jadx with Jadx("app.apk") as jadx: # All top-level classes (inner classes via cls.inner_classes) for cls in jadx.classes: print(cls.full_name, len(cls.methods), "methods") # Search by fully qualified name main = jadx.search_class("com.example.MainActivity") if main: print(main.code) # Method-level decompilation for method in main.methods: print(f"{method.name}: {method.code}") # Cross-references for method in main.methods: print(f"{method.name} called by: {[c.name for c in method.callers]}") # Package navigation for pkg in jadx.packages: print(pkg.name, len(pkg.classes), "classes") ``` ## 支持的输入 | 格式 | 示例 | |--------|---------| | APK | `Jadx("app.apk")` | | DEX | `Jadx("classes.dex")` | | Multi-DEX APK | 透明处理 —— 所有类已合并 | ## API 参考 ### `Jadx(path, *, threads=None, mode="auto", code_cache=False)` 主入口点。作为上下文管理器使用,或手动调用 `load()`/`close()`。 | 参数 | 描述 | |-----------|-------------| | `path` | APK 或 DEX 文件路径 | | `threads` | JADX 线程池大小 (`None` = CPU 核心数) | | `mode` | `"auto"`, `"restructure"`, `"simple"`, 或 `"fallback"` | | `code_cache` | `False` = 内存缓存 (默认), `True` = 磁盘缓存 | **属性:** | 属性 | 类型 | 描述 | |----------|------|-------------| | `classes` | `list[JavaClass]` | 所有 DEX 文件中的顶层类 | | `packages` | `list[JavaPackage]` | 包级别导航 | | `class_count` | `int` | 顶层类的数量 | | `java` | `JObject` | 底层 `jadx.api.JadxDecompiler` 的转义接口 | **方法:** | 方法 | 描述 | |--------|-------------| | `load()` | 加载并解析类(由 `__enter__` 调用) | | `close()` | 释放资源(由 `__exit__` 调用) | | `decompile_all()` | 预先反编译所有类 | | `search_class(name)` | 通过全限定名查找类 | ### `JavaClass` | 属性 | 类型 | 描述 | |----------|------|-------------| | `name` | `str` | 简称 (`"MainActivity"`) | | `full_name` | `str` | 全限定名 (`"com.example.MainActivity"`) | | `package` | `str` | 包名 | | `code` | `str` | 反编译后的 Java 源码 (惰性, 缓存) | | `smali` | `str` | Smali 反汇编代码 (惰性, 缓存) | | `methods` | `list[JavaMethod]` | 该类中的方法 | | `fields` | `list[JavaField]` | 该类中的字段 | | `inner_classes` | `list[JavaClass]` | 内部类/匿名类 | | `declaring_class` | `JavaClass \| None` | 父类 (如果是内部类) | | `top_class` | `JavaClass` | 最外层的外围类 | | `dependencies` | `list[JavaClass]` | 该类依赖的类 | | `is_inner` | `bool` | 是否为内部类 | | `java` | `JObject` | `jadx.api.JavaClass` 的转义接口 | ### `JavaMethod` | 属性 | 类型 | 描述 | |----------|------|-------------| | `name` | `str` | 方法名 | | `full_name` | `str` | 包含类的全限定名 | | `code` | `str` | 反编译后的源码 (惰性, 缓存) | | `callers` | `list[JavaMethod]` | 调用此方法的方法 (惰性, 缓存) | | `callees` | `list[JavaMethod]` | 此方法调用的方法 (惰性, 缓存) | | `override_related` | `list[JavaMethod]` | 双向重写链 | | `declaring_class` | `JavaClass` | 此方法所属的类 | | `java` | `JObject` | `jadx.api.JavaMethod` 的转义接口 | ### `JavaField` | 属性 | 类型 | 描述 | |----------|------|-------------| | `name` | `str` | 字段名 | | `full_name` | `str` | 包含类的全限定名 | | `type` | `str` | 字段类型的字符串表示 | | `callers` | `list[JavaMethod]` | 引用此字段的方法 (惰性, 缓存) | | `declaring_class` | `JavaClass` | 此字段所属的类 | | `java` | `JObject` | `jadx.api.JavaField` 的转义接口 | ### `JavaPackage` | 属性 | 类型 | 描述 | |----------|------|-------------| | `name` | `str` | 完整包名 | | `classes` | `list[JavaClass]` | 该包中的类 | | `sub_packages` | `list[JavaPackage]` | 子包 | | `java` | `JObject` | `jadx.api.JavaPackage` 的转义接口 | ## 转义接口 每个包装器都通过 `.java` 暴露其底层 JADX Java 对象。这允许你调用任何我们尚未包装的 JADX API: ``` with Jadx("app.apk") as jadx: cls = jadx.search_class("com.example.Foo") # Call JADX Java API directly code_info = cls.java.getCodeInfo() ``` ## JVM 共存 pyjadx 可以与同一进程中的其他 JPype 消费者(例如 [PyGhidra](https://github.com/NationalSecurityAgency/ghidra/tree/master/Ghidra/Features/PyGhidra)) 共存。如果 JVM 已经在运行,`pyjadx.start()` 会将 JADX JARs 添加到现有的 classpath 中。如果没有,它会启动一个 JVM。 ``` import pyghidra pyghidra.start() # starts JVM for Ghidra import pyjadx pyjadx.start() # adds JADX JARs to existing JVM — no conflict ``` ## 配置 **自定义 JADX 安装:** ``` # 通过环境变量 # export JADX_HOME=/opt/jadx # 或者在运行时 import pyjadx pyjadx.start(jadx_home="/opt/jadx") ``` **JAR 缓存:** JADX JARs 会在首次使用时从 Maven Central 自动下载,并缓存在 `~/.pyjadx/jars//`。 ## 许可证 MIT
标签:Android 安全, APK 分析, Dalvik 字节码, DAST, DEX 文件, JADX, Java 源码, JPype, JS文件枚举, Kotlin, Python, Python 绑定, 云安全监控, 云资产清单, 反编译器, 恶意软件分析, 无后门, 目录枚举, 移动安全, 自动化分析, 跨站脚本, 逆向工具, 逆向工程, 静态分析