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 绑定, 云安全监控, 云资产清单, 反编译器, 恶意软件分析, 无后门, 目录枚举, 移动安全, 自动化分析, 跨站脚本, 逆向工具, 逆向工程, 静态分析