ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

APK反编译工具链搭建:apktool、dex2jar、jd-gui与签名避坑

APK反编译工具链搭建:apktool、dex2jar、jd-gui与签名避坑

简介:面向Android逆向学习与安全审计场景的APK反编译工具合集,适合具备一定安卓基础、希望理解应用结构或开展二次开发的开发者使用。工具覆盖解包与资源还原、字节码转换、源码查看及重签名测试等环节,能够帮助读者分析清单文件、布局与字符串资源,并进一步定位混淆后的代码逻辑,理解界面设计与功能实现之间的对应关系。

压缩包共58个文件,约10.58MB,以批处理与命令行脚本、Java工具包、可执行图形程序及说明文档为主,脚本便于按流程调用,工具包与图形程序承担核心反编译和查看功能,文档则提供必要的操作提示。目前已有13978人学习,具有较高的参考热度。

对于需要系统练习APK逆向、排查自身应用风险或进行安全审计的读者,这套合集可减少逐个搜集与配置工具的时间,较快搭建从解包、转换到源码阅读的本地环境,并保留重打包签名的验证环节,便于对照分析资源改动与代码行为,也可用于学习他人的实现思路与结构组织。

1. APK 反编译工具装错版本的代价:这条链路上到底有几个组件

很多人拿到一个 APK,第一反应是装个 jd-gui 拖进去,结果窗口一片空白。APK 反编译工具从来不是单个软件,它是四条腿一起走的链路:apktool 负责拆资源和清单,dex2jar 把 classes.dex 转成 jar,jd-gui 负责看 Java 源码,auto-sign 或 apksigner 负责回编译之后再签回去。任何一条腿版本对不上,后面全白干。我见过最常见的翻车是把 JDK 17 当成万能钥匙,结果 dex2jar 在反射那一步抛一堆告警,jar 出来是残缺的,jd-gui 打开只看到空壳类。这套环境适合三类人:做逆向分析的、做老项目排障的、以及要给第三方 APK 打补丁的。下面按我实际装机的顺序,从 JDK 落位一路写到签名验证。

2. 环境与目录规划:JDK 版本和工具落位先定死

2.1 JDK 版本怎么选:三条工具链的交集

这四个工具里,有三个是 Java 写的,所以 JDK 是地基。麻烦在于它们对 Java 版本的口味并不一致。apktool 2.9 这条线官方要求 Java 8 以上,实际上在 11 和 17 上都跑得很稳;dex2jar 2.x 在 Java 8 上最安分,换到 17 之后部分混淆过的 dex 会在转 jar 时打出一串WARNING: Illegal reflective access,虽然多数情况还能出结果,但遇到加固样本就容易半途而废;jd-gui 自带反编译器,对 JDK 版本不敏感,但它吃内存,堆给小了打开大 jar 直接假死。

所以我的选择是:主用 JDK 17,同时在机器上留一套 JDK 8 备用,遇到 dex2jar 翻车就临时切过去。不追最新版本,JDK 21 上我实测过 apktool 的 wrapper 脚本没问题,但有几个老版本 dex2jar 的脚本会读取JAVA_HOME拼路径,路径里带-21-字样时行为不一致,没必要给自己找事。

Linux 下装两套运行时,命令很直接:

# Debian/Ubuntu 系:同时装 17 和 8,后面按需切换 sudo apt update sudo apt install -y openjdk-17-jdk openjdk-8-jdk # 确认安装后的真实目录名,JAVA_HOME 必须和这里一字不差 ls -1 /usr/lib/jvm/

这段命令的关键在第二条。/usr/lib/jvm/下的目录名在不同发行版、不同小版本上会变,有的是java-17-openjdk-amd64,有的是java-1.17.0-openjdk,直接抄别人的JAVA_HOME是新手最常犯的错。先ls看清,再写变量。

环境变量落到 shell 配置文件里:

# 写入 ~/.bashrc,注意路径替换成你 ls 出来的那个 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export PATH="$JAVA_HOME/bin:$PATH" # 备一套 8,用 jdk8 这个函数临时切,不动全局 jdk8() { export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export PATH="$JAVA_HOME/bin:$PATH" java -version }

PATH把$JAVA_HOME/bin放在最前面,是为了让java、keytool、jarsigner这几个命令始终跟着当前JAVA_HOME走。jdk8写成 shell 函数而不是脚本,是因为函数在当前 shell 里执行,export才会生效;写成独立脚本的话,子进程改完环境变量就退出了,父 shell 一点感觉都没有。这是很多人切版本切了个寂寞的原因。

macOS 上路径不用猜,系统自带查询工具:

# macOS 用 java_home 查询,输出直接就是可用的 JAVA_HOME /usr/libexec/java_home -V export JAVA_HOME=$(/usr/libexec/java_home -v 17)

-V列出机器上所有已安装的 JDK,-v 17按大版本筛选。Windows 则走系统属性里的环境变量面板,新建JAVA_HOME指向 JDK 根目录,再在Path里加一条%JAVA_HOME%\bin,注意别把bin写两遍。

装完必须验证,别信“应该好了”:

java -version javac -version echo $JAVA_HOME

三条都要有输出,且java -version里的版本号要和JAVA_HOME路径里的版本对得上。对不上说明 PATH 里有更靠前的 java,用which -a java找出来清掉。

2.2 工具链目录结构与 PATH 约定

我踩过的最没必要的坑,是把 apktool.jar 直接丢进/usr/local/bin。后果是半年后想升版本,已经不记得当初放的是哪个文件,也不敢删,怕删错。后来统一改成家目录下一个独立工具区,所有东西自己管自己:

# 一次性建好四个工具目录加一个工作区,工作区单独放,方便整体清空 mkdir -p ~/apk-tools/{apktool,dex2jar,jd-gui,sign,work}

目录职责我固定成这样,不做例外:

目录放什么是否进 PATH
~/apk-tools/apktoolapktool.jar与 wrapper 脚本是,只加脚本
~/apk-tools/dex2jar解压后的 dex-tools 全套是,加解压目录
~/apk-tools/jd-guijd-gui 的 jar 包否,用别名调用
~/apk-tools/signkeystore 与签名脚本是
~/apk-tools/work反编译产物、临时 dex、测试 APK否

PATH 只追加脚本目录,不把 jar 所在目录整个塞进去:

# 追加到 ~/.bashrc 末尾,只暴露可执行入口,不暴露一堆 jar export PATH="$HOME/apk-tools/apktool:$HOME/apk-tools/dex2jar:$HOME/apk-tools/sign:$PATH"

为什么不把~/apk-tools/jd-gui也加进去?因为那个目录里除了 jar 没别的,加了也没命令可用,反而在补全时多出一堆噪音。jd-gui 我用别名解决:

# 别名里固定堆大小,避免每次手敲 -Xmx alias jdgui='java -Xmx2g -jar $HOME/apk-tools/jd-gui/jd-gui.jar'

这种划分还有一个隐性好处:升级版本时只动一个目录,work 区里的历史产物不受影响;哪天整条链路要迁到另一台机器,把~/apk-tools打包拷过去,改一下JAVA_HOME就能用。工具链的可迁移性,靠的是目录约定,不是记忆。

3. 逐个装:四个组件按依赖顺序落地

3.1 apktool:jar 和 wrapper 两件套缺一不可

apktool 的发布形态是一个 jar 加一个启动脚本。jar 是本体,脚本是壳。很多人只下 jar,然后每次敲java -jar apktool.jar d xxx.apk,能用,但参数一多就容易漏-jar之外的东西,而且在不同目录下要写绝对路径。

先把 jar 放进目录并统一命名:

# 下载后重命名成固定名字,后续脚本和别名都引用这个名字 mv ~/Downloads/apktool_2.9*.jar ~/apk-tools/apktool/apktool.jar ls -lh ~/apk-tools/apktool/

重命名这一步是有意为之。版本号写在文件名里,看着清楚,但脚本里就得跟着改;固定成apktool.jar,升级时只覆盖文件,脚本一行不动。版本信息记在旁边的文本里,或者靠apktool --version查。

然后是 wrapper 脚本,Linux/macOS 版:

#!/usr/bin/env bash # apktool 启动壳:固定 jar 路径,把所有参数原样透传给 apktool exec java -Xmx1g -jar "$HOME/apk-tools/apktool/apktool.jar" "$@"

"$@"加引号是必须的,否则参数里带空格的路径会被拆成多段。exec让 java 进程替换掉当前 shell 进程,不多留一层壳,这样 Ctrl+C 的信号能直接打到 java 上,不会出现“脚本退了 java 还在跑”的僵尸情况。-Xmx1g是给反编译大 APK 留的余量,处理几百 MB 的资源包时默认堆会 OOM。

保存成~/apk-tools/apktool/apktool,给执行权限:

chmod +x ~/apk-tools/apktool/apktool apktool --version

Windows 下没有 shebang 这一套,用 bat:

@echo off REM %~dp0 表示脚本自身所在目录,保证在任意路径下双击都能找到 jar java -Xmx1g -jar "%~dp0apktool.jar" %*

%~dp0是 Windows 批处理里定位自身目录的标准写法,末尾自带反斜杠,所以后面直接拼文件名,不要再加一个。%*对应 bash 里的"$@",但 Windows 上对空格的转义本来就弱,路径里尽量别有空格。

3.2 dex2jar:解压、授权、验证入口

dex2jar 发布的是压缩包,解压即用,没有安装步骤,但有两个必做的动作:确认解压后的目录层级,以及给脚本补执行权限。

# 解压到工具目录,注意压缩包内层通常还套一层 dex-tools-2.4 unzip ~/Downloads/dex-tools-2.4.zip -d ~/apk-tools/dex2jar # 看一眼真实层级,别想当然 ls ~/apk-tools/dex2jar/

如果ls出来只有一个dex-tools-2.4目录,说明多套了一层,需要把里面的内容提上来,或者把 PATH 指到内层。我一般直接提上来,保持路径短:

# 内层目录内容上提一级,删掉空壳目录,路径稳定下来 mv ~/apk-tools/dex2jar/dex-tools-2.4/* ~/apk-tools/dex2jar/ rmdir ~/apk-tools/dex2jar/dex-tools-2.4

然后是权限。从压缩包解出来的.sh通常没有执行位:

# 给所有 sh 脚本补执行权限,然后跑一次 help 验证入口通不通 chmod +x ~/apk-tools/dex2jar/*.sh ~/apk-tools/dex2jar/d2j-dex2jar.sh --help

--help能打印出用法,说明 Java 环境和脚本本身都没问题。如果这一步报Permission denied,是权限没给上;报JAVA_HOME is not set或者No such file,是第 2 章的环境变量没生效,回去source ~/.bashrc。别在这一步就开始怀疑工具,九成是环境。

Windows 下对应的是.bat,不需要 chmod,但要注意解压工具可能给文件加了“来自网络”的锁定标记,右键属性里解除锁定,否则双击闪退。

3.3 jd-gui:免安装运行与内存参数

jd-gui 有两个发行版:一个是纯 jar,需要本机有 Java;一个是打包了运行时的可执行文件。我选纯 jar,理由是版本可控,跟着JAVA_HOME走,不会出现“系统里有两套 JRE,jd-gui 用了不认识的那个”这种事。

# 放到工具目录并统一命名,方便别名引用 mv ~/Downloads/jd-gui-1.6*.jar ~/apk-tools/jd-gui/jd-gui.jar java -Xmx2g -jar ~/apk-tools/jd-gui/jd-gui.jar

-Xmx2g不是随便写的。jd-gui 打开一个几十 MB 的 jar 时,会把所有类一次性读进内存建索引,堆给 512m 的话,界面会卡在加载条上不动,看起来像死机,其实是 GC 在疯狂挣扎。2g 是处理常规应用 jar 的舒适区,遇到特别大的包再往上加。

启动后不要急着拖文件,先在菜单里确认反编译引擎的选项。jd-gui 内置的反编译器对某些 Java 8 之后的语法支持一般,遇到 lambda 或者 switch 表达式会显示成乱码或者反编译失败,这是引擎能力边界,不是装错了。真要抠细节,后面再上更专业的反编译工具,jd-gui 的定位是快速浏览、找关键词、定位类。

3.4 auto-sign:密钥库和签名脚本

auto-sign 是一套老脚本,核心逻辑是调jarsigner给 APK 签名。它跑起来需要两样东西:一个 keystore,和一个能定位到 keystore 的脚本配置。

先造 keystore,用 JDK 自带的 keytool:

# 生成一个 100 年有效期的调试密钥库,口令统一成 android 方便脚本引用 keytool -genkeypair \ -keystore ~/apk-tools/sign/debug.keystore \ -alias debugkey \ -keyalg RSA \ -keysize 2048 \ -validity 36500 \ -storepass android \ -keypass android \ -dname "CN=debug, OU=dev, O=dev, L=dev, ST=dev, C=CN"

逐个参数说:-alias是密钥别名,签名时要对上;-keyalg RSA -keysize 2048是当前通用组合,别用 DSA;-validity 36500约一百年,避免调试包过期;-storepass和-keypass设成一样,是因为部分老脚本只读一个口令;-dname随便填,但格式必须是CN=...这种键值对串,少一个字段 keytool 会交互式追问,脚本化的时候就卡住了。

keystore 生成完,验证一下能读出来:

# 列出密钥库内容,确认别名和算法都对 keytool -list -v -keystore ~/apk-tools/sign/debug.keystore -storepass android

然后处理 auto-sign 脚本本身。这类脚本通常把 keystore 路径、别名、口令写死在文件头部,需要改成自己的:

# 在脚本头部定位这几个变量,改成实际路径和口令 # KEYSTORE="$HOME/apk-tools/sign/debug.keystore" # ALIAS="debugkey" # STOREPASS="android"

这里要说明一个取舍。auto-sign 走的是jarsigner,产出的是 v1 签名(JAR 签名)。Android 7.0 之后系统要求 v2 签名,只签 v1 的包在新系统上会报INSTALL_PARSE_FAILED_NO_CERTIFICATES。所以 auto-sign 现在只适合两个场景:快速验证回编译产物是否结构完整,以及给 Android 7 以下的老设备准备包。真要装到新手机上,末尾还得用 build-tools 里的 apksigner 补一道 v2。这个顺序不能反,v2 签完再跑 jarsigner,v2 签名块会被破坏。

如果只是想跑通链路,装到这里就够了。四个组件各就各位,剩下的都是配置。

4. 配置细节:反编译链路里那几个必须改的参数

4.1 apktool 的 framework 目录与 apktool.yml

apktool 拆 APK 时,资源文件(res 目录下的 XML)里的引用是数字 ID,要还原成人类可读的名字,靠的是 framework 资源表。系统级的 framework 没装,拆出来的布局文件里全是@2131427328这种鬼东西。

# 首次处理某类 ROM 的 APK 前,先把它对应的 framework-res.apk 装进本地仓库 apktool if framework-res.apk

if是 install-framework 的缩写,执行后会在~/.local/share/apktool/framework/下生成一个以包 ID 命名的 apk 文件。多个 framework 可以共存,apktool 按 APK 里声明的 ID 自动匹配。拆解时如果报Could not decode arsc file,八成是缺 framework 或者 apktool 版本比目标 APK 老,先补 framework,再考虑升级。

拆出来之后,根目录会有个apktool.yml,里面几个字段值得改:

# apktool.yml 关键字段,回编译时直接影响产物 sdkInfo: minSdkVersion: '21' # 低于目标机型的实际值会导致装上闪退 targetSdkVersion: '30' # 影响系统对权限、后台行为的判定 versionInfo: versionCode: '1' # 和原包不一致会导致覆盖安装失败 versionName: 1.0

minSdkVersion改小了没意义,改大了会拒绝安装在低版本设备上;versionCode是最容易被忽略的一条,回编译产物如果版本号和已安装的一致甚至更低,adb install会直接报INSTALL_FAILED_VERSION_DOWNGRADE,用-r -d强装又可能踩到别的坑。要么改大版本号,要么先卸载原包。

回编译命令本身也有讲究:

# -c 保留原始清单里的调试信息,-r 跳过资源重新编译 # 只改了 smali 时用 -r,速度提升非常明显 apktool b ~/apk-tools/work/smoke -o ~/apk-tools/work/rebuilt.apk -c

-c让 apktool 沿用原始 AndroidManifest 里的某些标记,避免重编后清单被改写;-r是纯 smali 修改场景的加速开关,跳过 aapt 那一段。但如果是改了布局或者 strings,就不能加-r,否则改动不生效。

4.2 dex2jar 的内存设置与编码

dex2jar 的入口脚本d2j-dex2jar.sh本身很薄,真正干活的参数在d2j_invoke.sh里。默认堆往往只给了 512m,处理大 dex 会直接 OOM。

# 把默认堆提到 2g,同时强制 UTF-8,避免中文常量变成问号 sed -i 's/-Xmx[0-9]*m/-Xmx2g/' ~/apk-tools/dex2jar/d2j_invoke.sh grep -n 'Xmx' ~/apk-tools/dex2jar/d2j_invoke.sh

用sed改而不是手改,是因为这个脚本可能有多处引用,改完用grep确认,看不到-Xmx2g就说明匹配模式没对上,需要打开文件看实际写法。编码问题更隐蔽:Java 默认用平台编码读 class 文件里的字符串常量,终端是 GBK 而 dex 里是 UTF-8 时,转出来的 jar 里中文全是问号。加一条启动参数:

# 在同一个脚本的启动参数里补上编码声明 # 找 java 调用那一行,加上 -Dfile.encoding=UTF-8

编码错乱不会报错,只会让 jd-gui 里看到一堆???,很多人以为是反编译失败,其实是转码阶段就烂了。这个坑我吃过一次,排查了两个小时。

4.3 签名对齐:zipalign 与 apksigner 的先后顺序

回编译产物要装进设备,签名是最后一道。这里有两个工具,顺序不能错:先对齐,再签名。

# 第一步对齐,4 字节边界是 Android 的硬要求,-p 让 so 文件也按页对齐 zipalign -v -p 4 ~/apk-tools/work/rebuilt.apk ~/apk-tools/work/aligned.apk # 第二步签名,v1 和 v2 都打开,兼顾新老设备 apksigner sign \ --ks ~/apk-tools/sign/debug.keystore \ --ks-pass pass:android \ --key-pass pass:android \ --ks-key-alias debugkey \ --v1-signing-enabled true \ --v2-signing-enabled true \ --out ~/apk-tools/work/signed.apk \ ~/apk-tools/work/aligned.apk

为什么顺序不能反:v2 签名是对整个 APK 文件做的摘要,一旦签完再动文件内容(对齐就是在动文件结构),摘要立刻失效,装上去就是INSTALL_PARSE_FAILED_NO_CERTIFICATES。这个错误的迷惑性在于,apksigner verify会告诉你签名有效,但安装就是失败,因为它验的是签名块,不是文件完整性。

验证签名要单独跑一条:

# 确认 v1 和 v2 两个方案都通过 apksigner verify --verbose --print-certs ~/apk-tools/work/signed.apk

输出里Verified using v1 scheme: true和Verified using v2 scheme: true两行是判断依据,缺一个都说明参数没生效。有些 build-tools 版本对--v2-signing-enabled的默认值和文档不一致,显式写出来最稳。

5. 避坑排查:五条血泪记录

这一章的五条,是我在不同机器、不同样本上真实撞过的。每条按现象、原因、解决三段说,照着对号入座就行。

5.1 安装阶段的命令找不到与权限被拒

第一条:apktool: command not found。现象是刚配完 wrapper 脚本,敲apktool提示找不到命令,但ls ~/apk-tools/apktool/明明有文件。 原因有两个可能:脚本没给执行权限,或者 PATH 追加后没重新加载。bash 只在启动时读一次.bashrc,export写在文件里但当前 shell 不知道。 解决是先chmod +x ~/apk-tools/apktool/apktool,再source ~/.bashrc,然后which apktool确认解析到的就是你那个脚本。如果which输出的是/usr/bin/apktool之类的路径,说明系统里装过旧版且优先级更高,把 PATH 追加顺序调到最前面。

第二条:d2j-dex2jar.sh: Permission denied。现象是脚本存在、Java 也正常,一执行就拒绝。 原因是 zip 压缩包不保存 Unix 执行位,解压出来的 sh 全是 644。 解决就是补权限chmod +x ~/apk-tools/dex2jar/*.sh。顺带一提,用unzip解压和用图形界面解压器解压,在权限上没区别,都得补这一步。

5.2 反编译阶段的解析失败与乱码

第三条:brut.androlib.AndrolibException: Could not decode arsc file。现象是apktool d跑到解析资源表就中断,堆栈里明确提到 arsc。 原因通常是两个:目标 APK 用了比当前 apktool 更新的资源格式,或者缺 framework 资源。 解决顺序是先apktool if framework-res.apk补框架,还不行就升 apktool 版本。反过来,如果 apktool 太新而去拆很老的包,也可能报AndrolibException: Could not decode arsc,这时候退回 2.4 那条线反而能过。版本不是越新越好,是越匹配越好。

第四条:拆出来的 strings.xml 中文全是乱码。现象是文件能打开,但中文变成测试这种。 原因是文件本身编码没问题,是终端或者编辑器的显示编码不是 UTF-8,或者 dex2jar 那一步没加编码参数导致 jar 里字符串已损坏。 解决分两步:先确认终端echo $LANG输出里带UTF-8;如果是 dex2jar 转出来的 jar 乱码,回到 4.2 节补-Dfile.encoding=UTF-8,重新转一次。已经转坏的 jar 救不回来,必须重跑。

5.3 回编译与签名阶段的装不上

第五条:回编译成功,安装报INSTALL_PARSE_FAILED_NO_CERTIFICATES。现象是apktool b输出 BUILD SUCCESSFUL,adb install直接被系统拒绝。 原因只有两类:压根没签名,或者只签了 v1 而设备是 Android 7 以上。 解决是用 apksigner 补 v2,并且严格保证“对齐 → 签名”的顺序。如果之前用 auto-sign 签过,必须从对齐那一步重来,不能在已签名的包上叠加,因为 v2 签名块会互相覆盖。 还有一种变体:签名都通过了,装上也成功,但一启动就闪退。那多半不是签名问题,是资源 ID 错位或者原包有完整性校验,需要回到 apktool 的-c参数和 smali 层面查,不要继续在签名上打转。

提示:这五条里有四条最终都指向环境或顺序,而不是工具本身有 bug。排查时先看which、java -version、echo $PATH这三条输出,能省掉一半时间。

6. 装完怎么验证:拿一个 APK 走完整条链路

装完不等于能用。我的验证方式很土:找一个体积不大的 APK,把四个组件串起来跑一遍,每一步都要看到预期输出,中间任何一步没有输出就是没通。

# 冒烟测试:四步串行,任何一步失败就停下来查,不要往下走 cd ~/apk-tools/work # 1. 拆包,能生成 smali 和 res 两个目录算过 apktool d -f -o ./smoke ./smoke.apk # 2. 单独取出 classes.dex,dex2jar 只认这个文件 unzip -o ./smoke.apk classes*.dex -d ./ # 3. 转 jar,看到 "Detail: ... -> ./classes.jar" 算过 ~/apk-tools/dex2jar/d2j-dex2jar.sh -f -o ./classes.jar ./classes.dex # 4. 打开 jar,能看到类树和反编译出的方法体算过 java -Xmx2g -jar ~/apk-tools/jd-gui/jd-gui.jar ./classes.jar

每一步的成功标志和失败时的第一排查点,我整理成一张表,贴在工位上:

步骤成功标志失败第一眼看哪里
apktool d生成smali/、res/、apktool.ymlapktool --version与 framework 是否装
unzip取 dex目录下出现classes.dexAPK 是否用了多 dex,改classes*.dex
d2j-dex2jar.sh输出Detail:行且无异常堆栈JAVA_HOME、-Xmx、-Dfile.encoding
jd-gui 打开左侧有包结构,右侧有方法体jar 大小是否为 0,堆是否给够

再补一条端到端的验证,把“拆了能装回去”这件事跑通:

# 回编译 + 对齐 + 签名,三步连做,最后 verify 两个 scheme 都是 true apktool b ./smoke -o ./rebuilt.apk -c zipalign -f -p 4 ./rebuilt.apk ./aligned.apk apksigner sign --ks ~/apk-tools/sign/debug.keystore --ks-pass pass:android \ --key-pass pass:android --ks-key-alias debugkey \ --v1-signing-enabled true --v2-signing-enabled true \ --out ./signed.apk ./aligned.apk apksigner verify --verbose ./signed.apk

这里有个我自己的习惯:验证用的 APK 永远不换,固定放在work目录下,名字就叫smoke.apk。因为换个样本就换了一堆变量,出了问题是工具坏了还是样本特殊,根本分不清。同一个样本反复跑,行为才可比。

还有个小技巧值得说:把上面这段验证写成~/apk-tools/verify.sh,每次升级任何组件之后先跑一遍,而不是直接拿真实目标样本去试。升级引入的回归问题,用冒烟脚本能在三十秒内暴露,用真实样本可能排查半天还以为是样本加固了。这个习惯是从一次 apktool 小版本升级后资源解析行为变化开始的——那次我直接拿业务样本试,拆出来的布局少了一半控件,查了整整一个下午,最后发现旧版本反而正常。

从那以后我每次动工具链,不管是升 JDK、换 apktool 版本还是重装 dex2jar,都强制走一遍这个冒烟脚本,四步全绿才碰真实样本。环境这东西,能复现才叫装好了,装好了不验证,等于没装。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进