同一个应用,从官方渠道下载和从某个第三方站下载,安装包大小可能差三倍。"为什么这个包比我朋友的大?"——这是很多人忽略的信号。体积差异背后,最常见的原因是混淆和加固。
这篇讲怎么用几条命令判断一个 APK 处于哪种状态:是正常编译、是被混淆过(正常,官方包也这样)、还是被加了壳或整体加固(可能是保护,也可能是黑产改包)。判断清楚之后,你对这个包的信任度才有依据。
一句话结论:混淆是"名字被改乱"(正常),加固是"代码被藏起来"(需要理由)。官方应用商店的包通常混淆但不加固;一个第三方包如果被深度加固,你就无法验证它到底做没做手脚。
很多人把这三件事混为一谈,其实它们的行为完全不同:
状态 | 做了什么 | 能否反编译出可读代码 | 是否常见 |
|---|---|---|---|
未处理 | 什么都没做 | 能,几乎原样 | 开发者自测包、古董应用 |
混淆(Obfuscation) | 类名/方法名改成 | 能,但读不懂名字 | 几乎所有正式发布包 |
加固/加壳(Packing) | 把真代码加密藏起来,运行时解密 | 不能,只有壳代码 | 商业加固服务、部分金融类 App |
整体保护 | 换成自定义加载器或虚拟机 | 不能 | 极少见,风险高 |
关键点:混淆不影响你的安全判断能力,因为你还能看到包内有哪些类、调用了哪些权限。加固会让你什么都看不到——而"看不到"这件事本身,既是保护也是遮掩。
jadx 是最省事的反编译器,有 GUI 版也有命令行版。
# 安装(macOS)
brew install jadx
# 打开 GUI
jadx-gui app.apk
# 或纯命令行导出源码
jadx -d out app.apk打开后看三个地方:
com.instagram.android.xxx 这种有意义的顶级包名,往下才是 a.b.c。sources/ 下大面积是 a、b、c、d 这种单字母目录,说明混淆过。这是正常现象。android/support、androidx 以外的异常大包。加固包常出现 com.secneo、com.tencent.StubShell、com.baidu.protect、com.stub.StubApp、libjiagu 这类名字。如果 jadx 打开后弹出大量 Failed to decompile 且几乎看不到任何业务逻辑,通常意味着加固。
加固的典型特征:dex 很少但类引用很多,或者 dex 头部异常小。
# 看 dex 文件大小分布
unzip -l app.apk | grep "classes.*dex"
# 用 apktool 看是否有第二个"隐藏" dex
apktool d app.apk -f -o apktool-out
ls -lh apktool-out/*.dex 2>/dev/null一个 50MB 的社交类 App,如果只有一个 classes.dex 且只有 200KB,这就是壳——真代码在别处(通常是 assets 里的加密文件或 lib/ 里的 .so)。
再查 lib/ 目录,加固服务几乎都会塞一个高度混淆的 native 库:
unzip -l app.apk | grep -E "\.so$" | head -20看到 libjiagu.so、libshella.so、libexec.so、libdexloader.so、libmobisec.so 这类名字,基本可以确定上了商业壳。
这一步不用装工具,strings 是系统自带的:
unzip -o app.apk -d extracted >/dev/null
strings extracted/classes.dex | grep -iE "jiagu|stub|bangcle|secneo|mobisec|protect|shell" | sort -u常见加固服务商的字符串指纹:
StubShell、libjiagulibjiagu、com.qihoobangcle、SecNeoijiami、ijmbaidu.protect、libbaiduprotectaliyun.mobile、mobisec看到这些不等于恶意,但如果一个声称"官方原版"的包带了这些,说明它不是从官方渠道拿的原始包——官方包通常不加固。
有了这些信息,怎么判断风险?分三档:
正常的正式发布包。你能看到权限、能看到 SDK 列表,能验证签名一致性。这是最理想的状态,绝大多数应用都在这一档。
很多银行、支付、企业 App 用了商业加固,这是合理的自身保护。判断方式:去官方渠道确认这个版本是否确实用了加固(有些应用开发者在 release notes 里会提),或者对比官方渠道的包是否也是同样的壳。
最需要警惕的组合。加固让你无法检查代码,来源不明让你无法验证签名。此时你只剩下三个判断依据:
apksigner verify --print-certs)aapt dump permissions app.apk)三条有一项不符,直接丢弃。
把上面几步合成一条命令,给下载的包做个体检:
#!/bin/bash
APK="$1"
echo "=== 包名与版本 ==="
aapt dump badging "$APK" | grep -E "^package|^application"
echo
echo "=== 权限 ==="
aapt dump permissions "$APK" | sed 's/^uses-permission: name=//'
echo
echo "=== 加固特征 ==="
unzip -o "$APK" -d /tmp/apkcheck >/dev/null 2>&1
strings /tmp/apkcheck/classes*.dex 2>/dev/null | grep -icE "jiagu|bangcle|secneo|ijiami|mobisec" | xargs echo "加固字符串命中次数:"
echo
echo "=== 签名 ==="
apksigner verify --print-certs "$APK" 2>/dev/null | head -10
rm -rf /tmp/apkcheck跑完看三行结论:包名对不对、权限有没有多、签名是不是官方的。这三行比任何"安全检测"网站都可靠,因为它们是你自己算出来的。
lib/ 里有没有加固壳 .so?strings 查过加固服务商字符串?前四项告诉你这个包的"体质",最后两项决定你能不能信任它。体质可以不完美,但签名和包名一旦不符,就没有讨论余地。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。