某app广告太多,看不下去了,逆一波

Posted 程序员启航

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了某app广告太多,看不下去了,逆一波相关的知识,希望对你有一定的参考价值。

声明:

​ 本篇只供学习,本人未用作商业用途。他人若未经允许,也不得使用以及参考本篇中提到的源码以及附件进行谋利,违者必究

本篇目的:

​ 由于工作原因,很长时间没搞破解了。几个月前只是简单对某镖游戏dump了il2cpp与meta-data,昨天试玩了一下,竟然要更新,fuck! fuck! fuck!

​ 然后随便在小米应用商店下载了个小游戏,广告太多,就以去广告为目标 练一下手吧。

游戏主页的最上方有一个广告栏:

点击礼物盒后启动了一个新的页面:

目标一:去除红色箭头所示的广告栏

目标二:礼物盒点击后去除广告

本篇用到的逆向工具

dex文件查看工具

Jeb(windows)

mac可参考这里

apktool2.5

1

2

3

java -jar ~/env/apktool_2.5.0.jar d app-debug.apk

java -jar ~/env/apktool_2.5.0.jar d -r app-debug.apk //Do not decode resources. 不反编译资源文件

java -jar ~/env/apktool_2.5.0.jar d -s app-debug.apk //Do not decode sources. 不反编译源码文件

Baksmali-2.5.2/smali-2.5.2

1

2

java -jar ~/env/baksmali-2.5.2.jar d classes4.dex //反编译dex文件,输出目录默认位out

java -jar ~/env/smali-2.5.2.jar a out //将out目录下面的smali源文件编译为dex文件,输出默认为out.dex

keytool

该cmd是为了生成keystore文件,重签名apk时需要。keytool命令在java安装目录/bin下面

1

keytool -genkey -alias mine.keystore -keyalg RSA -validity 30000 -keystore mine.keystore

jarsigner

该cmd是为了将apk进行重签名,重签名需要使用keystore文件。jarsigner命令也在java安装目录/bin下面

1

2

3

4

5

jarsigner -verbose -keystore mine.keystore -signedjar new.apk old.apk mine.keystore

-keystore:keystore 的名称

new.apk:签名后的apk

old.apk:签名前的apk

adb shell dumpsys activity activities top

打印手机上当前的未销毁的activity列表

adb shell dumpsys window windows

打印手机上当前的window,默认Z-order

游戏主页的广告栏分析与去除

将app启动,显示游戏主页,查看activity

​ dumpsys看看当前的广告栏属于哪个activity

activity为com.outfit7.mytalkingtomfree/com.jinke.Main

用jeb打开,Main--activity中看看没有相关的contentView之类的。

分析后,发现Main的父类中含有可疑的布局代码

上图中onCreate中调用了initContentLayout函数

1

2

3

4

5

6

7

private void initContentLayout() {

    WeakReference v0 = this.banneContainerReference;

    if(v0 == null || v0.get() == null) {

        this.banneContainerReference = new WeakReference(LayoutInflater.from(this.activityWeakReference.get()).inflate(layout.adjustable_banner_layout, this.findViewById(0x1020002), true));

        Log.d(MaoActivity.TAG, "setAdjustableBannerXY create view");

    }

}

这个函数中有一个Log.d,我们可通过查看程序运行日志确认该函数中的if语句是否执行了。

我这边在app冷启动时开始抓log,确实有这个日志

1

06-16 17:44:45.360 27784 27784 D com.outfit7.jinke.MaoActivity: setAdjustableBannerXY create view

或者大家也可以通过打断点调试,确认这里是否执行了。

代码拆分说明:

1

LayoutInflater.from(this.activityWeakReference.get()) 获取当前显示的activity,然后基于该activity对应的上下文创建一个LayoutInflater

1

inflate(layout.adjustable_banner_layout, this.findViewById(0x1020002), true));

LayoutInflater.inflate函数,代码加载布局,其函数声明为:

1

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

所以布局文件为layout.adjustable_banner_layout,这个布局的名字看起来就像是广告位。

看看广告位布局文件layout.adjustable_banner_layout

1

2

3

4

5

<?xml version="1.0" encoding="utf-8"?>

="vertical" android:id="@id/mao_adjustable_container_root" android:layout_width="fill_parent" android:layout_height="fill_parent"

  xmlns:android="http://schemas.android.com/apk/res/android">

    id="@id/mao_adjustable_ad" android:layout_width="wrap_content" android:layout_height="wrap_content" />

</RelativeLayout>

xml中整个view的layout_height为fill_parent,这个高度值可能太大了,看起来应该不是主页上方广告的布局。

通过调试查找可疑layout, 在MaoActivity的onResume处打个端点

直接查看当前Activity的DecorView,查看其children,发现0,0 1080,206这个boundary就是广告显示的位置

在jeb中看看com.miui.zeus.mimo.sdk.ad.banner.b{75aee83 VFE...C.. ......ID 0,0-1080,206}这玩意

b继承了FrameLayout

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class b extends FrameLayout {

    public class com.miui.zeus.mimo.sdk.ad.banner.b$1 implements View$OnClickListener {

        public com.miui.zeus.mimo.sdk.ad.banner.b$1(b arg1) {

            this.a = arg1;

            super();

        }

        public void onClick(View arg2) {

            b v2 = this.a;

            a v0 = v2.e;

            if(v0 != null) {

                v0.a(b.a(v2));

            }

        }

    }

    ...

    ...

    ...

}

继续查看这个类中有关初始化的函数,发现有一个函数调用了setOnClickListener。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//com.miui.zeus.mimo.sdk.ad.banner.b

private void a(View arg3) {

     if(this.d()) {

         this.b = arg3.findViewById(l.c("mimo_banner_view_summary"));

         this.f = arg3.findViewById(l.c("mimo_banner_border"));

         Glide.with(this.i).load(Integer.valueOf(l.b("mimo_banner_border"))).into(this.f);

         this.k = arg3.findViewById(l.c("mimo_banner_view_flipper"));

     }

     else {

         this.a = arg3.findViewById(l.c("mimo_banner_view_image"));

     }

     this.c = arg3.findViewById(l.c("mimo_banner_view_ad_mark"));

     this.d = arg3.findViewById(l.c("mimo_banner_view_close"));

     this.h = new d();

     this.d.setOnClickListener(new View$OnClickListener() {

         public void onClick(View arg1) {

             a v1 = this.a.e;

             if(v1 != null) {

                 v1.b();

             }

         }

     });

     ((FrameLayout)this).setOnClickListener(this.g);

 }

看看void a(View arg3)这个函数有哪些地方调用了

jeb中查看交叉引用非常方便,选中函数a,然后按下快捷键x,发现只有一个地方调用了a

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//com.miui.zeus.mimo.sdk.ad.banner.b

public void a(c arg3) {

      this.m = arg3;

      int v0 = com.miui.zeus.mimo.sdk.utils.a.a(arg3.Y());

      this.j = v0;

      if(v0 == 0) {

          this.j = l.a("mimo_banner_view_layout");

      }

      this.a(LayoutInflater.from(this.i).inflate(this.j, ((ViewGroup)this)));

      String v3 = arg3.M();

      if(TextUtils.isEmpty(((CharSequence)v3))) {

          this.b();

          return;

      }

      this.a(v3);

  }

这个函数中使用了LayoutInflator。 YES!!!!!无疑了,这个调用链肯定初始化了主页上方的广告位。

继续查找a(c arg3)的调用者

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

//com.miui.zeus.mimo.sdk.ad.banner

public void a(com.miui.zeus.mimo.sdk.server.api.c arg3, ViewGroup arg4, BannerInteractionListener arg5) {

        this.l = System.currentTimeMillis();

        String v0 = "BannerUIController";

        j.a(v0, "showBanner");

        this.e = arg3;

        this.h = arg5;

        if(arg3 == null) {

            this.a(com.miui.zeus.mimo.sdk.utils.error.a.e.au, com.miui.zeus.mimo.sdk.utils.error.a.e.av);

            j.b(v0, "Empty splash ad info view arguments");

            return;

        }

        this.f = arg4;

        this.c.post(new Runnable(arg3) {

            public void run() {

                try {

                    c.a(this.b, new b(c.a(this.b)));

                    c.b(this.b).a(this.b);

                    c.b(this.b).a(this.a);

                }

                catch(Exception v0) {

                    j.b("BannerUIController""Failed to create view", ((Throwable)v0));

                    this.b.a();

                }

            }

        });

    }

这个函数中的字符很给力,showBanner, Failed to create view等

a(com.miui.zeus.mimo.sdk.server.api.c arg3, ViewGroup arg4, BannerInteractionListener arg5)的调用者

1

2

3

4

5

6

7

//com.miui.zeus.mimo.sdk.ad.banner

public void a(ViewGroup arg3, BannerInteractionListener arg4) {

    this.f = arg3;

    b v3 = new b(this, arg4);

    this.q = v3;

    this.g.a(this.d, this.f, ((BannerInteractionListener)v3));

}

a(ViewGroup arg3, BannerInteractionListener arg4)的调用者

1

2

3

4

5

6

7

8

//com.miui.zeus.mimo.sdk.BannerAd

public void showAd(ViewGroup arg3, BannerInteractionListener arg4) {

        if(arg3 == null) {

            j.b("BannerAd""showAd failed, container can not be null");

        }

        this.mAdImpl.a(arg3, arg4);

    }

showAd(ViewGroup arg3, BannerInteractionListener arg4)的调用者

1

2

3

4

5

6

7

8

9

10

11

12

13

public void onBannerAdLoadSuccess() {

    String v0 = "MiMoAdBannerAdapter";

    MLog.w(v0, "load banner ad success ,start render");

    if(MiMoAdBannerAdapter.access$100(this.a) != null) {

        MLog.w(v0, "start show banner ad");

        MiMoAdBannerAdapter.access$100(this.a).showAd(MiMoAdBannerAdapter.access$200(this.a), MiMoAdBannerAdapter.access$300(this.a));

    }

    else {

        MLog.w(v0, "load banner ad success,but banner ad is null");

        this.a.notifyLoadError(new MMAdError(-2000));

        this.a.trackDspLoad(String.valueOf(-2000), null);

    }

}

​ 上述调用栈关系也可以使用动态调试的方式,一步到位。我没有使用调试的方法,因为这个广告在我的手机上出现的概率太低,想调试一次太难了,大家理解一下哈。

基于上述分析,去掉主页上方广告位的思路:

源头去除: onBannerAdLoadSuccess函数中,showAd调用移除。缺点:可能影响了app原本逻辑

尾部去除:设置view的visibility。优点:基本不影响app原本逻辑

昨天调试的时候保存了广告栏显示的代码源头。大家可以自行分析一下,看是否跟我找到的一样。

1

2

3

4

5

//com.xiaomi.ad.mediation.mimo.MiMoAdBannerAdapter

public void loadAndShow(AdInternalConfig arg1, AdLoadAndShowListener arg2, AdLoadAndShowInteractionListener arg3) {

        super.loadAndShow(arg1, arg2, arg3);

        AndroidUtils.runOnMainThread(this.mMainHandler, new MiMoAdBannerAdapter$a(this, arg1));

    }

1

2

3

4

5

6

7

8

9

10

11

public class MiMoAdBannerAdapter$a implements Runnable {

    public MiMoAdBannerAdapter$a(MiMoAdBannerAdapter arg1, AdInternalConfig arg2) {

        this.b = arg1;

        this.a = arg2;

        super();

    }

    public void run() {

        this.b.loadBannerAd(this.a);

    }

}

直接将run函数return,实测有效

1

2

3

4

5

6

7

java -jar ~/env/baksmali-2.5.2.jar d classes2.dex

cd out/com/miui/zeus/mimo/sdk/ad/banner

vim a\\$3.smali

//去掉invoke-virtual。相当于run()函数什么都没有做

还有一种方案:模拟广告位close的方法,对app逻辑无任何影响,大家自行尝试

点击礼物盒跳转至广告activity,如何disable该功能

老办法,先用dumpsys看看activity是哪个

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

admin@C02D7132MD6R bin % adb shell dumpsys activity activities

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)

Display #0 (activities from top to bottom):

  Stack #1:

     ...

     ...

      * Hist #1: ActivityRecord{1f34e49 u0 com.outfit7.mytalkingtomfree/com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity t3}

          packageName=com.outfit7.mytalkingtomfree processName=com.outfit7.mytalkingtomfree

          launchedFromUid=10139 launchedFromPackage=com.outfit7.mytalkingtomfree userId=0

          app=ProcessRecord{382ce36 4930:com.outfit7.mytalkingtomfree/u0a139}

          Intent { cmp=com.outfit7.mytalkingtomfree/com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity (has extras) }

          ...

          ...

          resizeMode=RESIZE_MODE_RESIZEABLE

      * Hist #0: ActivityRecord{64ced92 u0 com.outfit7.mytalkingtomfree/com.jinke.Main t3}

          packageName=com.outfit7.mytalkingtomfree processName=com.outfit7.mytalkingtomfree

          launchedFromUid=10139 launchedFromPackage=com.outfit7.mytalkingtomfree userId=0

          app=ProcessRecord{382ce36 4930:com.outfit7.mytalkingtomfree/u0a139}

          Intent { flg=0x10000000 cmp=com.outfit7.mytalkingtomfree/com.jinke.Main (has extras) }

RewardVideoAdActivity与主页(Main)不一个activity,暴力拦截startActivity可行?

先挂断点查看startActivity调用栈, 我断在了instrumentation->execSartActivity方法

很可惜,尝试了头部去除以及尾部去除,重打包运行会有两个问题:

1.activity确实被拦截掉了,但是页面会卡在一个view中,显示加载中...

2.按返回键尝试将卡view的界面关掉,礼物盒没有正常开启奖励,肯定是影响了游戏逻辑。

奖励没拿到。。。。毛用都没有

继续硬头皮找关键点吧

广告activity显示有一个特点,close按钮隐藏后显示

广告加载完之后,右上角有一个X按钮,会显示出来。未加载完的时候隐藏。所以就在Activity中找找有没有相关的close或者onClick关键字

jeb中直接找到RewardVideoAdActivity, 确实搜索到了close与onclick关键字

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity

private void k() {

    int v0 = com.miui.zeus.mimo.sdk.utils.a.c(this.i.Y());

    if(v0 == 0) {

        v0 = l.a("mimo_reward_view_end_page_landscape");

    }

    this.h = LayoutInflater.from(((Context)this)).inflate(v0, this.g, true);

    String v0_1 = this.i.M();

    String v1 = this.i.L();

    Bitmap v0_2 = BitmapFactory.decodeFile(v0_1, this.o);

    Bitmap v1_1 = BitmapFactory.decodeFile(v1, this.o);

    this.h.findViewById(l.c("mimo_reward_flv_video")).setImageBitmap(v0_2);

    this.h.findViewById(l.c("mimo_reward_icon")).setImageBitmap(v1_1);

    this.h.findViewById(l.c("mimo_reward_title")).setText(this.i.h());

    this.h.findViewById(l.c("mimo_reward_summary")).setText(this.i.g());

    this.h.findViewById(l.c("mimo_reward_dsp")).setText(this.i.i());

    View v0_3 = this.h.findViewById(l.c("mimo_reward_jump_btn"));

    ((TextView)v0_3).setText(this.i.O());

    b v1_2 = new b();

    this.y = v1_2;

    ((com.miui.zeus.mimo.sdk.anim.a)v1_2).b(v0_3).a(1200).a(-1).b(1).a(new AccelerateDecelerateInterpolator()).c();

    this.h.findViewById(l.c("mimo_reward_close_img")).setOnClickListener(((View$OnClickListener)this));

    this.g.setOnClickListener(((View$OnClickListener)this));

}

void k()这个函数中使用了LayoutInflater,“mimo_reward_close_img”, setOnClickListener(this)。太明显了,毫无压力。

再考虑一个问题,何时广告播放完显示X按钮呢

直接搜关键字setVisibility,该函数会将一个view显示出来

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

//com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity

private void p() {

    int v0;

    if(this.i.c()) {

        this.n();

        if(!TextUtils.isEmpty(this.i.u())) {

            v0 = 1;

        }

        else {

            goto label_11;

        }

    }

    else {

    label_11:

        v0 = 0;

    }

    if(v0 != 0) {

        this.m();

    }

    else {

        this.f.setVisibility(8);

        this.k.setVisibility(8);

        this.g.setVisibility(0);

        b v0_1 = this.y;

        if(v0_1 != null) {

            ((com.miui.zeus.mimo.sdk.anim.a)v0_1).c();

        }

        ViewFlipper v0_2 = this.u;

        if(v0_2 != null) {

            v0_2.stopFlipping();

        }

        com.miui.zeus.mimo.sdk.utils.analytics.b.a(this.i.m(), this.i, "END_PAGE_VIEW""load_success"0, "");

    }

}

搜到了,就在RewardVideoAdActivity代码中,代码还是那么的明显,毫无压力。

this.g.setVisibility(0),这里的0是一个flag, 就是将view显示出来。

在RewardVideoAdActivity.p()函数打断点,调试一把,看看调用栈

源头是通过onCompletion调过来的, com.miui.zeus.mimo.sdk.video.TextureVideoView$3实现了MediaPlayer.OnCompletionListener接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public class com.miui.zeus.mimo.sdk.video.TextureVideoView$3 implements MediaPlayer$OnCompletionListener {

    public com.miui.zeus.mimo.sdk.video.TextureVideoView$3(TextureVideoView arg1) {

        this.a = arg1;

        super();

    }

    public void onCompletion(MediaPlayer arg2) {

        TextureVideoView.c(this.a, 5);

        TextureVideoView.d(this.a, 5);

        if(TextureVideoView.e(this.a) != null) {

            TextureVideoView.e(this.a).hide();

        }

        if(TextureVideoView.h(this.a) != null) {

            TextureVideoView.h(this.a).onCompletion(TextureVideoView.d(this.a));

        }

    }

}

原来广告是通过MediaPlayer播放的,然后播放是否完成 是 通过MediaPlayer的回掉接口掉过来的。

模拟MediaPlayer播放完成,模拟点击close按钮,实现插件

首先要拿到MediaPlayer实例

手有点疼了。。。。

主要是通过jeb,查看交叉引用关系

MediaPlayer是TextureVideoView的成员

1

2

3

4

5

6

//com/miui/zeus/mimo/sdk/video/TextureVideoView

public Map m;

public int n;

public int o;

public Surface p;

public MediaPlayer q;

TextureVideoView是VideoAdView的成员

1

2

3

4

5

6

//com.miui.zeus.mimo.sdk.video.VideoAdView

public TextureVideoView a;

public ImageView b;

public FrameLayout c;

public boolean d;

public static final String e = "VideoAdView";

VideoAdView是RewardVideoActivity的成员

1

2

3

4

5

6

7

public class RewardVideoAdActivity extends Activity implements View$OnClickListener, a {

    public static final String a = "key_baseadinfo";

    public static final String b = "RewardVideoAdActivity";

    public static final String c = "key_exposure";

    public static final long = 60000;

    public EventRecordFrameLayout e;

    public VideoAdView f;

结论: 可以通过拿到RewardVideoActivity实例,然后反射一步步拿到MediaPlayer实例

如何拿到RewardVideoActivity实例

这可能是个老生常谈的问题。方式可能有好几种吧, 下面是我能想到的

1: ActivityThread中保存了当前进程的activity列表

1

final ArrayMap mActivities = new ArrayMap<>();

2.拦截ActivityThread.H,这玩意是一个Handler,第一时间通过system_server将activity的生命周期,回掉过来。

3.通过frida或者其他hook工具,在activity->onCreate或者onResume这里下手

4.通过注入Instrumentation的方式,管控activity生命周期,可以拦截到onCreate以及onResume等

我使用了最后一种方案,用java写了点代码,最后编译为smali文件后,通过静态代码注入重打包了apk。

贴代码

instrumentation注入:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public static void initInstrumentation() {

    final String CLASS_ACTIVITYTHREAD = "android.app.ActivityThread";

    RefUtils.MethodRef<Object> currentActivityThreadRef =

            new RefUtils.MethodRef<Object>(CLASS_ACTIVITYTHREAD, true,

                    "currentActivityThread", new Class[0]);

    Object activityThread = currentActivityThreadRef.invoke(null, new Object[0]);

    RefUtils.FieldRef instrumentationFieldRef =

            new RefUtils.FieldRef(CLASS_ACTIVITYTHREAD, false, "mInstrumentation");

    Instrumentation appInstru = instrumentationFieldRef.get(activityThread);

    HackerInstrumentation.INSTANCE().setOriginInstru(appInstru);

    instrumentationFieldRef.set(activityThread, HackerInstrumentation.INSTANCE());

}

Instrumentation实体类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

public class HackerInstrumentation extends Instrumentation {

    private Instrumentation mOrigin = null;

    public static HackerInstrumentation INSTANCE() {

        return DefaultHackerInstru.get();

    }

    private HackerInstrumentation(){}

    private static class DefaultHackerInstru {

        private static HackerInstrumentation sInstancce = new HackerInstrumentation();

        static HackerInstrumentation get() {

            return sInstancce;

        }

    }

    public void setOriginInstru(Instrumentation instru) {

        mOrigin = instru;

    }

    @Override

    public void callActivityOnCreate(Activity activity, Bundle icicle) {

        mOrigin.callActivityOnCreate(activity, icicle);

        TalkingTomHacker.afterCreateActivity(activity);

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

    @Override

    public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {

        mOrigin.callActivityOnCreate(activity, icicle, persistentState);

        TalkingTomHacker.afterCreateActivity(activity);

    }

    @Override

    public void callActivityOnDestroy(Activity activity) {

        mOrigin.callActivityOnDestroy(activity);

        TalkingTomHacker.afterCreateActivity(activity);

    }

    @Override

    public void callActivityOnResume(Activity activity) {

        mOrigin.callActivityOnResume(activity);

        TalkingTomHacker.afterResumeActivity(activity);

    }

}

在callActivityOnCreate回掉中拿到activity实例,然后通过反射拿到MediaPlayer

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

public static void afterCreateActivity(Activity activity) {

    Log.d(TAG, "afterCreateActivity " + activity);

    try {

        disableRewardActivityMediaPlayer(activity);

    } catch (Exception e) {

        e.printStackTrace();

    }

}

private static void disableRewardActivityMediaPlayer(Activity activity) throws Exception {

    final String REWARD_ACTIVITY_NAME = "com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity";

    if (!TextUtils.equals(activity.getLocalClassName(), REWARD_ACTIVITY_NAME)) {

        return;

    }

    final String VIDEO_AD_VIEW_FIELD_TYPE = "com.miui.zeus.mimo.sdk.video.VideoAdView";

    Field tmpRef = null;

    Field[] fields = activity.getClass().getDeclaredFields();

    for (Field field : fields) {

        if (TextUtils.equals(field.getType().getName(), VIDEO_AD_VIEW_FIELD_TYPE)) {

            tmpRef = field;

            break;

        }

    }

    if (tmpRef == null) {

        return;

    }

    tmpRef.setAccessible(true);

    Object videoAdView = tmpRef.get(activity);

    if (videoAdView == null) {

        Log.e(TAG, "videoAdView is null");

        return;

    }

    final String TEXTURE_VIDEO_VIEW_TYPE = "com.miui.zeus.mimo.sdk.video.TextureVideoView";

    tmpRef = null;

    fields = videoAdView.getClass().getDeclaredFields();

    for (Field field : fields) {

        if (TextUtils.equals(field.getType().getName(), TEXTURE_VIDEO_VIEW_TYPE)) {

            tmpRef = field;

            break;

        }

    }

    if (tmpRef == null) {

        return;

    }

    tmpRef.setAccessible(true);

    TextureView textureVideoView = (TextureView) tmpRef.get(videoAdView);

    if (textureVideoView == null) {

        Log.e(TAG, "textureVideoView null");

        return;

    }

    final String MEDIA_PLAYER_TYPE = "android.media.MediaPlayer";

    tmpRef = null;

    fields = textureVideoView.getClass().getDeclaredFields();

    for (Field field : fields) {

        if (TextUtils.equals(field.getType().getName(), MEDIA_PLAYER_TYPE)) {

            tmpRef = field;

            break;

        }

    }

    if (tmpRef == null) {

        return;

    }

    TextureView.SurfaceTextureListener originListener = textureVideoView.getSurfaceTextureListener();

    if (originListener == null) {

        Log.e(TAG, "origin surface texure Listener null");

        return;

    }

    setOldTextureListener(originListener);

    textureVideoView.setSurfaceTextureListener(sSurfaceTextureListener);

    final Field mediaPlayerRef = tmpRef;

    ...

    ...

}

模拟MediaPlayer播放完成

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

HandlerUtils.post(new Runnable() {

    @Override

    public void run() {

        sModifyMediaPlayerCondition.block();

        sModifyMediaPlayerCondition.close();

        mediaPlayerRef.setAccessible(true);

        MediaPlayer player = null;

        try {

            player = (MediaPlayer) mediaPlayerRef.get(textureVideoView);

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        }

        if (player == null) {

            Log.e(TAG, "MediaPlayer is null");

            return;

        }

        player.pause();

        try {

            //模拟MediaPlayer播放完成

            pretendCompleteMediaPlayer(player);

        } catch (Exception e) {

            e.printStackTrace();

        }

        player.release();

    }

});

模拟点击close按钮,关闭广告activity

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

private static void disableRewardActivityResume(Activity activity) {

    final String REWARD_ACTIVITY_NAME = "com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity";

    if (!TextUtils.equals(activity.getLocalClassName(), REWARD_ACTIVITY_NAME)) {

        return;

    }

    synchronized (TalkingTomHacker.class) {

        if (activity.isDestroyed() || activity.isFinishing()) {

            return;

        }

        pretendClickCloseActivity(activity, "mimo_reward_close_img");

    }

}

private static void pretendClickCloseActivity(Activity activity, String resStr) {

    if (activity instanceof View.OnClickListener) {

        int resId = activity.getApplicationContext().getResources().getIdentifier(resStr, "id", activity.getPackageName());

        if (resId <= 0) {

            Log.e(TAG, "pretendClickCloseActivity failed for res " + resStr + " not found");

            return;

        }

        View closeLikelyView = new View(activity.getApplicationContext());

        closeLikelyView.setId(resId);

        ((View.OnClickListener) activity).onClick(closeLikelyView);

        Log.d(TAG, "pretendClickCloseActivity success");

    else {

        Log.e(TAG, "pretendClickCloseActivity failed for not OnClickListener instance");

    }

}

代码写完,写一个接口,方便静态代码注入

以上是关于某app广告太多,看不下去了,逆一波的主要内容,如果未能解决你的问题,请参考以下文章

搞定接口测试变态要求:海量接口返回值对比验证

Discuz论坛广告横幅大图在百度app内无法显示,百度app默认开启了广告屏蔽

好学习法

Android 杀后台太狠,谷歌:看不下去了,势必要揪出“凶手”!

为啥ios设置启动图不显示不出来

[Python]常用代码块

(c)2006-2024 SYSTEM All Rights Reserved IT常识