重打包安卓包:装得上、能开,服务端为什么还是不认它

经验 2026-09-24 13:15 · 59 次阅读 ·APK,签名,apktool,逆向,踩坑

同一个 App,重打包前后:逻辑没改,签名指纹全变了

背景

手上有个 160 MB 的安卓包(没有源码,只有安装包),我想改两处:一是弱网下的网络超时太长,二是清单里一堆「申请了也拿不到」的权限。没有源码,就只能拆包、改、重打包、重签名。

拆之前先用 aapt2 dump badging 把它量清楚:

package: name='com.XXXXXX' versionCode='81943' versionName='8.19.43'
sdkVersion:'21'   compileSdkVersion='33'

zip 内部 3592 个条目、4 个 dex、清单里 53 行 uses-permission。体量决定了后面每一步都慢:apktool 解包、打包各一趟十几分钟。

做法或实测

一、先量,再改

想改超时,得先知道原值是多少,否则「改小了」纯靠感觉。写脚本扫 smali,找 setConnectTimeout / setReadTimeout 的调用,再回溯上几行的 const 拿实际数字:

CALL  = re.compile(r'->set(Connect|Read|Write)Timeout\(I\)V')
CONST = re.compile(r'\bconst(?:/4|/16|/high16)?\s+v\d+,\s*(0x[0-9a-fA-F]+|-?\d+)')

for i, ln in enumerate(lines):
    m = CALL.search(ln)
    if not m:
        continue
    for j in range(i - 1, max(-1, i - 9), -1):   # 往回找 8 行
        c = CONST.search(lines[j])
        if c:
            记录取值; break

实测:扫 30773 个 smali 文件、耗时 28.7 秒,能识别出取值的一共 30 处:

超时类型 取值 出现次数
Read 60000 ms 7
Connect 10000 ms 8
Connect 5000 ms 6
Connect 20000 ms 3
Read 20000 ms 2
Connect / Read 15000 / 5000 / 2500 各 1

结论:读超时最大是 60 秒,连接超时主流是 10 秒 —— 跟我一开始猜的「一律 20 秒」完全不同。没这一步,改的就是一堆凭印象的数字。

二、改两处

权限:删掉 12 行,全是「申请了也没用」的:

  • 权限名根本不存在:ACCESS_FIND_LOCATION(正确写法是 ACCESS_FINE_LOCATION)
  • 签名级拿不到:READ_LOGS、INTERACT_ACROSS_USERS、MOUNT_UNMOUNT_FILESYSTEMS、WRITE_APN_SETTINGS
  • 早就废弃:RESTART_PACKAGES、GET_TASKS

超时:setReadTimeout 60000 → 20000,setConnectTimeout 20000 → 10000。

三、重打包 + 签名

java -jar apktool.jar b src -o out_unsigned.apk --use-aapt2
zipalign -f -p 4 out_unsigned.apk out_aligned.apk
apksigner sign --ks my.keystore --out out.apk out_aligned.apk
apksigner verify -v out.apk

顺序不能反:zipalign 必须在签名之前,签完再对齐会破坏 v2 签名块。

实测对比(同一台机器、同一次改动):

项目 原包 重打包后
体积 167,737,045 B(160.0 MB) 169,288,278 B(161.4 MB)
zip 条目 3592 3540
uses-permission 53 41
签名证书 CN=guiqi.yzy, OU=hema CN=paodan, OU=dong
证书 SHA-256 57a7d49d… 431c5f55…
签名方案 v1 ✓ v2 ✓ v3 ✗ v1 ✓ v2 ✓ v3 ✓

四、出包后先自检,再发给别人

z = zipfile.ZipFile('out.apk')
print('损坏条目:', z.testzip(), '| 条目数:', len(z.namelist()))

再用 aapt2 dump badging 核对包名、版本号、权限数对不对,然后算一次 MD5 记下来 —— 手机端装完出问题时,这个指纹是唯一能确认「装的是不是同一个包」的东西。

另外,160 MB 的包别指望聊天工具传:IM 的文件上限普遍在 30 MB 左右,走 HTTP 直链让手机浏览器下载才靠谱。

结论与踩坑

1. 装得上、能开 ≠ 还能用。 重签的包装进手机后启动正常,但一登录就报「网络异常」—— 本地功能全好,只有需要联网的操作失败。可以确认的第一个事实是:重打包后签名证书的 DN 和 SHA-256 全变了。对服务端来说,换了签名的包就是另一份软件,做签名校验的服务完全可以据此拒绝。

2. 定位方法:零改动对照实验。 别急着回滚代码。先造一个「原包一个字都不改」的对照组 —— 只解包、重新打包、重新签名,装上去跑一次:

  • 对照组也失败 → 问题出在「重新签名」这件事本身,跟你的改动无关
  • 对照组正常 → 问题在你的改动,再逐项回滚

想把变量分得更干净,还可以只换签名、不重新打包:用 zipfile 逐条搬运原包内容,只剥掉旧签名文件(META-INF/*.SF、*.RSA、MANIFEST.MF),再对这个「内容逐字节相同」的包重签。这样对照组里唯一的变量就是签名。

3. 顺手记下的坑:

  • apktool 重打包会让 zip 条目数变化(3592 → 3540)、体积涨约 1.4 MB,不是文件被吃掉了
  • 新版 apksigner 默认会比原包多打一层 v3 方案(原包只有 v1+v2),apksigner verify -v 一眼能看出来
  • jadx 传 --threads 会直接报 ERROR - Incorrect arguments: Unknown option: --threads,网上抄的参数不一定存在
  • 签名用的 keystore 丢了,同名应用就再也没法升级 —— 留好备份

4. 边界。 这套流程适用于自有应用和学习研究;有服务端签名校验的第三方 App,改完签名基本过不了它的校验。未经授权修改别人的商用应用有法律风险,别越线。

继续看

全部 →