前言
最近在使用乐播投屏时遇到一个奇怪现象:手机端App播放没有任何广告,但一旦投屏到电视上,就会弹出十几秒甚至一分钟的广告。这个现象引发了我的好奇------既然手机端没广告,那广告逻辑一定在电视端。
本文记录了使用MT管理器逆向分析乐播投屏电视版APK的全过程,包括技术思路、关键发现、遇到的坑,以及最终的解决方案。希望能给同样遇到此问题的朋友一些参考。
环境准备
-
工具:MT管理器(需要root权限或Shizuku服务)
-
目标:乐播投屏电视版APK
-
测试环境:安卓电视(实际分析在模拟器中完成)
第一阶段:定位广告Activity
使用Activity记录功能
MT管理器自带"Activity记录"工具,可以实时捕捉应用打开的所有界面。操作步骤如下:
-
打开MT管理器 → 工具 → Activity记录 → 开始记录
-
切换到乐播投屏,执行触发广告的操作(投屏到电视)
-
等广告完整显示后,切回MT管理器停止记录
记录结果显示了一个关键Activity:
text
com.hpplay.sdk.sink.business.BusinessActivity
初步分析BusinessActivity
用MT管理器打开APK,进入Dex编辑器++,找到这个类的代码。初步看下来,这个类本身没什么特殊,只是一个继承自Activity的普通类。但继续往下看,发现了关键线索:
smali
# instance fields
.field private iActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
这个iActivity是一个接口类型,而BusinessActivity的所有生命周期方法(onCreate、onResume等)都在调用它:
smali
.method protected onCreate(Landroid/os/Bundle;)V
...
iget-object v0, p0, Lcom/hpplay/sdk/sink/business/BusinessActivity;->iActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
invoke-interface {v0, p1}, Lcom/hpplay/sdk/sink/bpi/IActivity;->onCreate(Landroid/os/Bundle;)V
...
.end method
关键发现 :BusinessActivity只是一个"壳",真正的业务逻辑全部在IActivity的实现类中。也就是说,广告逻辑也藏在那里。
第二阶段:追踪IActivity的实现
查看IActivity接口定义
搜索Lcom/hpplay/sdk/sink/bpi/IActivity;,找到接口定义:
smali
.class public interface abstract Lcom/hpplay/sdk/sink/bpi/IActivity;
.super Ljava/lang/Object;
# virtual methods
.method public abstract onCreate(Landroid/os/Bundle;)V
.method public abstract onResume()V
.method public abstract onPause()V
.method public abstract onDestroy()V
.method public abstract onKeyDown(ILandroid/view/KeyEvent;)Z
...(共50+个抽象方法)
.end method
这是一个定义了所有Activity生命周期和交互方法的"大接口",广告和正常投屏功能都通过它实现。
找到IActivity的赋值位置
BusinessActivity中的iActivity是从哪里来的?看onCreate方法的第一行:
smali
invoke-static {}, Lcom/hpplay/sdk/sink/business/LelinkManager;->getInstance()Lcom/hpplay/sdk/sink/business/LelinkManager;
move-result-object v0
iget-object v0, v0, Lcom/hpplay/sdk/sink/business/LelinkManager;->iActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
iput-object v0, p0, Lcom/hpplay/sdk/sink/business/BusinessActivity;->iActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
原来iActivity是从LelinkManager这个单例中获取的。于是找到LelinkManager类:
smali
.class public Lcom/hpplay/sdk/sink/business/LelinkManager;
.super Ljava/lang/Object;
# static fields
.field private static a:Lcom/hpplay/sdk/sink/business/LelinkManager;
# instance fields
.field public iActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
.field public iBPI:Lcom/hpplay/sdk/sink/bpi/IBPI;
.field public iService:Lcom/hpplay/sdk/sink/bpi/IService;
.field public iTipActivity:Lcom/hpplay/sdk/sink/bpi/IActivity;
# methods
.method public static declared-synchronized getInstance()Lcom/hpplay/sdk/sink/business/LelinkManager;
...
.end method
新发现 :LelinkManager只是一个单例持有者,它定义了iActivity、iBPI、iService等接口,但没有在构造方法中初始化它们。这意味着这些接口是在其他地方被创建并赋值的。
第三阶段:陷入困境
到了这一步,逆向进入了深水区:
-
找不到IActivity的具体实现类 :搜索
.implements Lcom/hpplay/sdk/sink/bpi/IActivity;没有结果,类名可能被混淆了。 -
需要追踪整个初始化流程 :需要找到是谁、在什么时候给
LelinkManager的成员变量赋值,这需要分析大量代码。 -
修改风险极高:即使找到广告逻辑所在的类,它也不是一个简单的if判断,而是与网络请求、状态管理、计时器等多个模块耦合。修改一处可能导致连锁崩溃。
-
电视系统恢复困难:在电视上安装一个调试失败、频繁崩溃的修改版APK,可能导致电视系统卡顿、无法投屏,甚至需要恢复出厂设置。
最终解决方案
鉴于逆向的复杂度和风险,我放弃了自行修改的方案,转而寻找社区现成的修改版。
在"ZNDS智能电视网"和"当贝市场"论坛搜索 "乐播投屏电视版去广告" 或 "乐播投屏纯净版",可以找到网友修改好的版本。安装前在电视设置中开启"允许安装未知来源应用"即可。
效果:安装后投屏广告完全消失,功能正常。
经验总结
-
理解现象背后的原理:手机端无广告、电视端有广告,是因为广告逻辑在电视端的投屏服务中,修改手机端App无效。
-
逆向思路要清晰:从Activity记录定位目标 → 分析代码架构 → 追踪接口实现 → 定位赋值位置,每一步都需要耐心。
-
评估投入产出比:当逆向遇到复杂架构、代码混淆时,需要评估继续深入的成本。对于普通用户,寻找现成修改版是更高效的选择。
-
安全第一:逆向修改后的APK务必先在模拟器测试,再考虑安装到电视。电视系统恢复比手机麻烦得多。
后记
虽然这次逆向没有成功修改广告,但通过这个过程,我深入了解了乐播投屏的SDK架构设计------通过IActivity接口代理Activity生命周期,实现业务逻辑的解耦。这个经验对理解Android插件化、组件化架构也有一定帮助。