ARTICLE · 1244049
App 开屏广告很烦,受不了我用ai把app广告去掉了:完整实操记录
拆掉摄像 App 开屏广告:完整实操记录
摘要:自己动手去掉一款摄像头配套 App 的开屏广告和首页横幅,完整记录三个大坑与全部解法。
一句话结论
花两个小时,把一款家用摄像头配套 App 的开屏广告和首页横幅全去掉了。
成品能装、能用,摄像头实时画面正常,连续 10 轮启动零广告。
真正费时间的不是改代码,是三个坑:
加固库在 JNI_OnLoad 里校验证书,重签名必闪退;
广告通路有三条,只堵一条不算完;
首页角标上的文字根本不在 APK 里,而在 Dart 编译产物中,用 UTF-16 存的。
这篇是实操记录,适合想自己清理 App 广告、又不想装来路不明"去广告版"的人。
我拿它干了什么
目标是朋友手机上的一款摄像头配套 App,套娃安装。
点开图标先来一段全屏广告。广告点一下,还会被拉起应用市场的推广页。
关掉之后,首页下半部分挂着一块横幅,右上角一个"免广告"的角标。
我拿它做的具体任务是:产出一个自用安装包。
只改这一个 App,不动系统,不装 Xposed 框架,不装 root 方案。
环境是一台安卓手机(Android 16)。
工具链是 baksmali、smali、zipalign、apksigner 这几件套,加上 adb 无线调试。
结果与数据
先看效果。
下面是同一部手机、同一个滚动位置,修补前后各截一张。左边那块橙色「免广告」角标和下方的横幅,补丁打完之后一起消失了。

▲ 修补前后对比
(图里摄像头实时画面和广告创意都做了打码,全图不含可识别信息。)
广告一共有三条路
我一开始以为只有一条。
拉出 7 个 dex 逐个扫类名,才看清结构。

▲ 三条广告通路示意
第一条,开屏广告。
应用自己的开屏页里直接 new 了一个广告平台的请求对象,然后调 loadAd()。
广告加载完会跳到应用市场的推广详情页,再从详情页跳到广告平台的承接页。
堵住这个 loadAd(),整条链就断了。
第二条,聚合广告 SDK 的横幅。
Dart 侧通过 Flutter 插件下发,插件再调原生 SDK 的横幅视图。
第三条,还是那个平台,但走的是原生广告接口,由应用自己的 Activity 直接调用。
我第一轮只堵了第二条。
原因是 Dart 侧的事件名全是那家 SDK 的风格,我下意识就顺着它查了。
装到手机上发现横幅还在,只是换了个样式。
这条教训是:定位广告通路,别看上层事件名,要去扫底层 SDK 接口的调用方。
谁在调那个 API,一眼就能看出来。
闪退:加固库在校验证书
改完第一版装上去,直接闪退。
每次启动约 2 秒必崩,系统的崩溃报告里有完整堆栈:
java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in ".../base.apk!/lib/arm64-v8a/libXXXXXXX.so" at com.xxx.AppCrypto.<clinit> 这是加固保护库。
它的 JNI_OnLoad 会读 APK 的签名证书,跟自己内置的期望值比一比。对不上就返回 JNI_ERR,System.loadLibrary 随之抛异常。
静态初始化一失败,整个进程就崩了。
我在那个 so 的字符串表里翻到了它内置的期望值——一串大写十六进制的字符,正好是原始签名的证书 MD5。
改法是这样:把我们自签名的证书 MD5 写进同一个位置,等长替换。
因为长度一模一样,so 的体积没变,APK 里这个条目的偏移和 4K 页边界都不用重新算。
这一条差点让我前功尽弃。
我一开始的判断是"加固导致启动异常的概率低",实际是必然触发。
角标的文字不在 APK 里
广告内容去掉后,首页还剩一个"免广告"的橙色角标。
它是应用自己的界面,不是广告素材:一张底图、一张小图标、一行文字。
前两样好办,换成同尺寸全透明图就行。
麻烦的是那行文字。我在整个 APK 和编译产物里搜 UTF-8,一个字都没有。
原因在 Flutter 的 AOT 编译机制。
非拉丁字符不走 UTF-8,走 UTF-16。而且短字符串可能是长字符串的切片共享——"免广告"正好是"免广告特权"的前缀。
按 UTF-16 去搜才命中,一共两处。
最后在编译产物里把那 6 个字节原地换成 3 个空格,文件长度一个字节没变。
改完实测 Dart 运行时没有报任何错,启动正常。
过程中的其他意外
- 手机的 logcat 在这台机器上基本读不出东西。20 秒启动窗口只有 550 字节,还是一段无关的加密头。没有阳性对照,logcat 就不能用来判断广告有没有出现,最后是靠抓前台 Activity 加抓进程列表定的性。
- 无线调试的 mDNS 序列号中间带空格。用空格切分会把序列号截断一半,后面每条 adb 命令都报找不到设备。
- smali 汇编会丢掉注释。所以"我打了补丁"这种标记在回编译后就没了,自检只能查方法体里还有没有实际调用。
收尾状态
同一把密钥签的包之间可以覆盖安装。
所以中途换版本不用卸载,用户的数据一条没丢。
完

▲ 改包流水线示意
四个环节,每个都能单独跑:
- 拆:baksmali 把 dex 反编译成 smali 文本,随便用编辑器看
- 改:按锚点定位目标方法,替换成自己要的逻辑
- 合:smali 汇编回 dex,回编译后自检方法体里还有没有目标调用
- 装:换 dex 重打包 → zipalign 排页 → 签名 → 覆盖安装
环节拆得越细越好定位问题。我把每一步都做成了脚本,版本升级后重跑一遍就行。
适用与不适用
谁该用
- 自己手机上那几个碍事的开屏广告,而且愿意自己折腾:改一个 dex、重新打包、签名、覆盖安装,全程有脚本,重复一次十几分钟。
- 想学一下加固库怎么校验签名的开发者:我把定位过程和修复方式都写清楚了,等长替换这个思路还能用在你遇到的其他保护库上。
- 需要可复现流程的团队:整条流水线是脚本,版本升级后重跑一遍就行。
谁别用
- 想省事直接装"去广告版"的人:那些包大多带未知代码,还常把原 App 的数据导走,自己改反而干净。
- 需要上架、签名一致、多设备同步的用户:重签名会让应用市场拒绝升级,后续每次更新都得重新来一遍。
- 靠付费去广告买权益的用户:绕过权益属于违反服务条款,这条我不做,也没写进方法里。
- 完全不懂命令行的人:流程里有 baksmali、smali、zipalign、签名证书,缺一步都可能装不上。
几句提醒
我只改了自己手机上那个自用 App。
没有传播原始安装包,也没有动任何别人的设备。
技术上还有几个已知限制。
加固库一旦换版本、换了校验算法,这套等长替换就要重新定位。
应用内的激励视频和插屏广告这条流程没覆盖。
AOT 字符串如果改成远端下发,字节替换也会失效。
需要完整脚本和流程的话,评论区扣个"脚本",我把可复现的版本整理出来。















































































