apkbox
APK 工具箱:把 APK 转成能直接引入工程的 AAR,反编译出 .java,或快速看一眼 APK 里有什么。
apkbox app.apk # app.apk → app.aar(aar 是默认子命令)
apkbox java app.apk -o src/ # 整个 APK 反编译成 .java
apkbox info app.apk # 包名 / 权限 / 组件 / ABI / 签名摘要
依赖
- Python 3.9+
- JDK 8+(
java要在PATH里) - 可选:Android SDK —— 提供
android.jar做 AAR 的资源 link 自检,也是 aapt2 的首选来源(见下文)
就这些。wheel 只有约 40 KB、Python 侧无第三方依赖;apktool / dex-tools / aapt2 / jadx 这些工具不在包里,系统装过就直接用,没有的话首次用到时自动下载到缓存 (约 110 MB,一次性)。
安装
pip install apkbox # 从 PyPI 装
# 或从源码 / wheel 装
pip install --break-system-packages .
pip install --break-system-packages dist/apkbox-2.1.3-py3-none-any.whl
# 不想安装,直接在源码目录跑
python3 -m apkbox --version
Debian/Ubuntu 的系统 Python 是 externally-managed,所以要加
--break-system-packages; 在 venv 里装则不需要这个参数。
工具从哪来
每个工具按这个顺序解析,找到就用,不重复造轮子:
命令行参数 > 环境变量 > 系统 PATH / Android SDK > apkbox 缓存 > 自动下载
-
缓存目录默认
~/.cache/apkbox(遵循XDG_CACHE_HOME),下载前先比对 sha256,装一次后续直接复用。 -
下载先走官方直链(GitHub releases、Google Maven),不通再换内置镜像; 设置
APKBOX_MIRROR可以指定自己的镜像(此时镜像优先、直链兜底)。 -
首次运行会看到类似输出,之后都是
✓:tools : apktool ✓, dex2jar 已下载, aapt2 ✓ -
想提前把工具备齐(离线机、CI 预热):
apkbox fetch # 全部:apktool / dex2jar / aapt2 / jadx apkbox fetch jadx apktool # 指定几个
fetch每个工具下载后都会回验:能跑的打✓,跑不起来的打⚠并说明原因 (退出码 1)——不会下载完就报成功。
平台注意(Termux / ARM)
apktool / dex-tools / jadx 都是纯 Java,跟着 JDK 走;只有 aapt2 是平台相关的, Google 官方只提供 x86_64 Linux 版(没有 arm64,没得下)。在 ARM 设备(Termux、 树莓派、Apple Silicon)上:
- apkbox 会先自己想办法(都不需要 root,
fetch和转 AAR 都会触发):- Termux:问一句后执行
pkg install aapt2 - Debian / Ubuntu 等带 apt 的发行版:用
apt --print-uris解析依赖,把官方 仓里的 aapt2 及其依赖下载、解包进缓存(走你自己配置的 apt 源,glibc 天然匹配)
- Termux:问一句后执行
- 也可以手动装,装上就自动识别、优先使用:Termux
pkg install aapt2、 Debian/Ubuntuapt install aapt2,或用--aapt2/APKBOX_AAPT2指一个能跑的 - 或装
qemu-x86_64模拟跑官方版(apkbox 检测到会自动用) - 都不行时不会白下 2.3 MB,直接说清原因(官方版是 x86_64 二进制 / 本机
架构不符)并给上面的解法:
fetch打 ⚠ 且退出码 1;转 AAR 降级继续—— 跳过 aapt2 自检,R.txt/public.xml缺失、资源 id 不固定(有警告),其余 产物(classes.jar / res / manifest)照常可用
环境变量:
| 变量 | 作用 |
|---|---|
APKBOX_MIRROR |
镜像前缀,逗号分隔,下载地址 = 前缀 + 官方 URL(与 gh-proxy.com 同规则);设为空则只走直链 |
APKBOX_OFFLINE=1 |
禁止联网,缺工具直接报错并给出手动下载地址 |
APKBOX_CACHE |
缓存目录(默认 ~/.cache/apkbox) |
APKBOX_NO_VERIFY=1 |
跳过 sha256 校验(慎用) |
APKBOX_APKTOOL / APKBOX_DEX2JAR / APKBOX_AAPT2 / APKBOX_JADX |
分别指定工具路径 |
ANDROID_HOME / ANDROID_SDK_ROOT |
找 aapt2 和 android.jar |
APKBOX_LANG |
界面语言 zh / en |
当前自动下载的版本(升级时改 apkbox/fetch.py 里的 SPECS):
| 工具 | 版本 | 来源 |
|---|---|---|
| apktool | 2.7.0 | GitHub releases(单个 fat jar) |
| dex-tools | 2.4 | GitHub releases |
| jadx | 1.5.6 | GitHub releases |
| aapt2 | 9.4.1-15978811 | Google Maven(dl.google.com) |
转 AAR
apkbox app.apk # → app.aar(和输入同目录)
apkbox aar app.apk -o dist/app.aar --keep # 指定输出,保留中间文件
apkbox app.apk --code-only # 只要 classes.jar,不带 res/assets/jni
apkbox app.apk -w work/ --skip-decode # 复用 work/ 里已有的解包结果
流程:apktool 解包 → dex2jar 出 classes.jar(两者互不依赖,并行执行,整体快约 1–2 分钟)→ 组装 res/assets/jni(public.xml 钉死资源 id)→ 裁剪 AndroidManifest → aapt2 compile + link 自检 → 打包 .aar。
常用选项:
| 选项 | 说明 |
|---|---|
-o, --output |
输出 .aar 路径(默认与 apk 同名) |
-w, --work-dir |
中间文件目录(默认临时目录,指定后保留) |
--code-only |
只要 classes.jar,不带 res/assets/jni |
--keep-app-attrs |
保留 <application> 的属性(默认清空以免与宿主合并冲突) |
--skip-decode |
复用 -w 目录里已有的 apktool 解包结果 |
--keep |
保留临时目录 |
--apktool / --dex2jar / --aapt2 |
手动指定工具(默认按上面的顺序自动解析) |
--android-jar |
android.jar 路径(用于 link 自检,缺省自动找 SDK) |
-v, --verbose |
打印每步实际执行的子命令 |
产物注意事项
- 打印的
可能与宿主 Duplicate class: androidx …是提示:这些包宿主大概率已有,合并前先看一眼。 <application>的android:name/icon/label/theme默认被清空(避免和宿主合并冲突),图标、label、主题、Application 类要宿主自己设置;确实想带上用--keep-app-attrs。- 没找到 android.jar 时跳过 link 自检,
R.txt按原 APK 的资源 id 生成,minCompileSdk取原 APK 的compileSdkVersion;找到时会尝试各平台,取能通过的最低版本。 - native 库按 APK 原样携带:APK 里有哪些 ABI,AAR 的
jni/就有哪些;只有 arm 库的话 x86 模拟器跑不起来(会打印警告)。
反编译成 .java
apkbox java app.apk -o src/ # APK → src/**/*.java + resources/
apkbox java classes.dex # 单个 dex → classes-java/
apkbox java ./smali_out/ -o src/ # 目录里挑 classes*.dex
apkbox java app.apk -r # --no-res:只要代码,不解资源
apkbox java app.apk -j 4 --deobf # 4 线程 + 反混淆
| 选项 | 说明 |
|---|---|
-o, --output |
输出目录(默认 <输入名>-java) |
-r, --no-res |
不反编译资源,只出 .java |
-j, --jobs |
线程数(默认用 jadx 的) |
--deobf |
反混淆:太短/太长的类名重新命名 |
-e, --export-gradle |
输出成 Android/Java gradle 工程 |
--show-bad-code |
反编译不出来的代码也照样输出(带注释标记) |
--jadx |
jadx 可执行文件/jar(默认自动解析/下载) |
- 有反编译错误时会在输出旁写一份
<输出名>.log(混淆过的 APK 出现几百处错误很常见)。 - 进度行只在终端里刷,重定向到文件时不会刷屏。
看 APK 摘要
apkbox info app.apk
apkbox info v2.1.3
file : app.apk (21.2 MB)
sha256 : 7440d842…
dex : 3 文件, 15.1 MB
res : 766 条目, assets 98, arsc 3.3 MB
abis : arm64-v8a
sign : v1 + v2/v3
package : com.deepseek.chat (versionCode 279, versionName 2.6.1)
label : DeepSeek
sdk : min 23, target 36, compile 36
launcher : com.deepseek.chat.MainActivity
perms : 22
- android.permission.INTERNET
…
components: 32 (activity 11, service 11, receiver 5, provider 5)
- zip 级信息(dex / res / ABI / 签名)纯 Python 读,秒出;manifest 级信息用
aapt2 dump badging+xmltree,没有 aapt2 时自动降级为 zip-only 摘要并提示。 - 组件统计数的是
AndroidManifest.xml里的 activity / service / receiver / provider(含 activity-alias)。 - 不解包、不反编译,适合 CI 里做前置检查。
体积分析
apkbox size app.apk # 按类别统计 + 最大的 10 个文件
apkbox size app.aar --top 20 # .aar 同样可用
apkbox size v2.1.3
file: app.apk (21.2 MB), 1023 个文件, 压缩后 21.0 MB (原始 43.5 MB, 2.1x)
类别 文件数 压缩后 占比 原始 膨胀
代码 11 7.2 MB 34.3% 15.1 MB 2.1x
原生库 16 8.7 MB 41.4% 20.5 MB 2.4x
资源 767 3.9 MB 18.3% 4.1 MB 1.1x
…
最大的 5 个文件(压缩后):
1. classes.dex 5.7 MB → 11.7 MB
…
- 类别口径:dex/jar/kotlin → 代码;
res/+resources.arsc→ 资源;lib//jni/→ 原生库;AndroidManifest.xml→ 清单;META-INF/+R.txt 等 → 元数据。 - 纯 zip 读,秒出;"膨胀"列是原始大小 ÷ 压缩后大小。
对比两个包
apkbox diff old.apk new.apk # 版本升级前后 / CI 里做回归检查
- zip 级:文件增减、内容变化(按未压缩大小,同内容重新打包不算差异)、 各类别体积增减、大文件清单。
- manifest 级:包名/版本、sdk、label、launcher、权限增减、组件数量变化。
AAR 直接解析纯文本 manifest;APK 走
aapt2 dump,没有 aapt2 时跳过该段并提示。 - 退出码
0完全一致 /1有差异 /2输入错误,可直接用于 CI 卡点。
列 dex 类名
apkbox classes app.apk # 全部类名
apkbox classes app.apk com.foo # 子串过滤
apkbox classes app.apk -c # 只要数量
apkbox classes classes.dex kotlin. # 单个 dex 也行
- 纯 Python 解 dex(class_defs → type_ids → string_ids),不解包、不反编译,秒出。
.dex/.apk/ 含classes*.dex的 zip 都支持;多 dex 按 classes.dex, classes2… 顺序合并。- 统计行走 stderr(
24 / 15484 类),stdout 可以直接管道给 grep/wc。
dex 成员与统计
apkbox members app.apk MainActivity # 这些类的字段/方法(类名子串匹配)
apkbox members app.apk 'okhttp3.' # 一批类的成员一起看
apkbox dexinfo app.apk # 每个 dex 的大小/类/方法/字段数
members输出类名#方法(参数)返回值/类名#字段:类型,参数按 dex 描述符 还原成 Java 写法(android.os.Bundle、int[]),和 javap 的签名一致。dexinfo的"方法"列是method_ids数——65536 上限算的就是它;单个 dex 接近 60000 会打警告(混淆重的大包很容易撞上)。- 都是纯 Python 解 dex,统计行走 stderr,stdout 可直接管道。
清单转 XML
apkbox manifest app.apk # 二进制清单 → 可读 XML(走 aapt2)
apkbox manifest app.aar # 文本清单 → 重新排版
apkbox manifest app.apk -o m.xml # 写到文件
- 输出标准 XML(带 xmlns 声明、属性全部展开),可以直接 diff、搜索、给脚本吃; 不解包、不反编译。
- APK 需要 aapt2(自动解析/下载);AAR 或纯文本 XML 不需要,走 ElementTree 美化。
组件审计
apkbox components app.apk # 列四大组件: 类型/exported/类名/权限
apkbox components app.apk --exported # 只看导出的(攻击面)
apkbox components app.apk --type service # 只看某一类
apkbox components app.aar # AAR/文本清单不需要 aapt2
exported列是生效值:显式android:exported优先;没声明时带 intent-filter 的标default*(旧规则默认可导出),否则-。- 统计行(
共 n 个组件 (…)) 走 stderr,stdout 是纯数据可直接管道;组件带 permission / readPermission / writePermission 时行尾追加[权限]。
XML 条目转文本
apkbox xml app.apk res/xml/network_security_config.xml
apkbox xml app.apk res/layout/main.xml -o out.xml
- 任意二进制 XML 条目(layout / xml / drawable…)→ 可读 XML,和
manifest同一条解码链路;文本条目直接美化输出。 manifest就是xml … AndroidManifest.xml的特例。
提取图标
apkbox icon app.apk # → app-icons/<dpi 目录>/
apkbox icon app.apk -o icons/ # 指定输出目录
- 优先
aapt2 dump badging里的application-icon-<dpi>行,每个 dpi 精准取; 没有 aapt2 时退回文件名启发式(*ic_launcher*)并提示。 - 按原目录结构落到输出目录;
adaptive-icon的 xml 是二进制,想看内容用apkbox xml。
完整性体检
apkbox check app.apk # zip / CRC / dex / 清单 / 签名
- 纯 Python 不联网:zip 结构 → 全量 CRC → dex magic → 清单在不在 → v1 / v2-v3 签名,逐行打 ✓/✗/⚠。
- ✗(损坏/缺失)退出码 1;⚠(没 dex、没签名,AAR 很常见)只提示,退出码 0。
找字符串
apkbox strings app.apk --only dex https # 只扫 dex,正则过滤
apkbox strings app.apk --only native -i libfoo
apkbox strings classes.dex --min 6 # 单文件,unix strings 风格
- 语义同 unix
strings:stdout 出串(zip 输入带条目名:前缀),stderr 出 统计,直接管道给 grep。 - 扫 APK 时按条目解压后逐个扫,
--only限定类别(dex/res/assets/native/…); 只匹配 ASCII 可打印串,--min默认最短 4,--icase忽略大小写。
只抽一类文件
apkbox extract app.apk native -o libs/ # 只要 .so(或 dex/res/assets/…)
apkbox extract app.apk manifest # manifest 文本(见下)
apkbox extract app.aar all -o out/ # 整包解出
apkbox extract app.apk all --list # 只列出条目(大小+路径),不写文件
apkbox extract app.apk all --glob 'lib/*' # 通配符过滤,和类别条件叠加
- 类别:
dex/res/assets/native/manifest/meta/all, 默认输出到<输入名>-<类别>/。 - 防 zip-slip:拒绝
..和绝对路径条目(会提示跳过了几个)。 --list不建目录不写文件;--glob是 fnmatch 通配符(*/?/[seq])。manifest对 APK 用aapt2 dump xmltree转成AndroidManifest.txt(文本格式, 不是 XML 源文件);没有 aapt2 时退成原始二进制并提示。AAR 的 manifest 本就是 文本,原样抽出。想直接拿可读 XML 用apkbox manifest。
界面语言
按系统 locale 自动选中文/英文,也可以强制指定:
apkbox --lang en java app.apk # --lang 放子命令前后都行
APKBOX_LANG=en apkbox app.apk # 环境变量
Python API
from apkbox import (convert, decompile_to_java, run_info, run_size,
run_diff, run_classes, run_members, run_dexinfo,
run_manifest, run_xml, run_components, run_icon,
run_check, run_strings, run_extract, ToolError)
convert("app.apk") # → app.aar,返回退出码
convert("app.apk", "dist/app.aar", code_only=True) # 只要 classes.jar
info = decompile_to_java("app.apk", "src/") # 返回统计 dict
print(info["java_files"], info["errors"], info["log_file"])
run_info("app.apk") # 打印 APK 摘要,返回退出码
run_size("app.apk") # 体积分析
run_diff("old.apk", "new.apk") # 0 一致 / 1 有差异 / 2 输入错误
run_classes("app.apk", "com.foo") # 类名(stdout 打印)
run_members("app.apk", "MainActivity") # 字段/方法
run_dexinfo("app.apk") # 每 dex 类/方法/字段数
run_manifest("app.apk") # 清单转可读 XML
run_xml("app.apk", "res/xml/network_security_config.xml") # 任意条目转可读 XML
run_components("app.apk", exported_only=True) # exported 组件审计
run_icon("app.apk") # 抽启动图标
run_check("app.apk") # 完整性体检
run_strings("app.apk", "https", only="dex") # 在 dex 里找字符串
run_extract("app.apk", "native", "libs/") # 抽原生库
run_extract("app.apk", "all", list_only=True) # 只列出条目
参数与命令行同名(convert 的 work_dir/code_only/keep/skip_decode/...,
decompile_to_java 的 no_res/deobf/jobs/show_bad_code/...)。
工具级失败抛 ToolError;API 和命令行一样,缺工具会自动下载(APKBOX_OFFLINE=1 可禁止)。
退出码
| 码 | 含义 |
|---|---|
| 0 | 成功(diff 里表示两包完全一致;check 的 ⚠ 只提示不算失败) |
| 1 | 工具执行失败(外部命令报错,已打印 error: …);diff 里表示有差异;check 发现损坏/缺失;icon 没找到图标 |
| 2 | 参数/输入文件问题 |
Metadata
Release files for apkbox 2.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| apkbox-2.1.3.tar.gz | 75.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| apkbox-2.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 156.8 kB
Release files / apkbox-2.1.3.tar.gz
| Download URL | apkbox-2.1.3.tar.gz |
|---|---|
| Size | 75.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2a74406482fc3ed7cc6574835174f801b045fbb05733e6a24f11e2b5a6075fe8
|
|
BLAKE2b-256 checksum How to use checksums |
b6d9034b5ad3e0bb3b0c13e9106233b58dda25b8b66b8c7d8ebc90e96b98728d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / apkbox-2.1.3-py3-none-any.whl
| Download URL | apkbox-2.1.3-py3-none-any.whl |
|---|---|
| Size | 80.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7d2692661f88bbcb3b07ff97cc9fa8483ff286f95ce73a984f78c7672e16dcf
|
|
BLAKE2b-256 checksum How to use checksums |
f77e3b63df6e7441a2232c455cb17ac14360c24222fa4159c7477a5156e1d270
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|