广告类型

启动广告 弹窗&更新广告 横幅广告

安卓四大组件

组件 描述
Activity(活动) 在应用中的一个Activity可以用来表示一个界面,意思可以理解为“活动”,即一个活动开始,代表 Activity组件启动,活动结束,代表一个Activity的生命周期结束。一个Android应用必须通过Activity来运行和启动,Activity的生命周期交给系统统一管理。在一个应用中我们所有能看到的界面都是Activity。
Service(服务) Service它可以在后台执行长时间运行操作而没有用户界面的应用组件,不依赖任何用户界面,例如后台播放音乐,后台下载文件等。
Broadcast Receiver(广播接收器) 一个用于接收广播信息,并做出对应处理的组件。比如我们常见的系统广播:通知时区改变、电量低、用户改变了语言选项等。
Content Provider(内容提供者) 作为应用程序之间唯一的共享数据的途径,Content Provider主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的接口。Android内置的许多数据都是使用Content Provider形式,供开发者调用的(如视频,音频,图片,通讯录等)

Activity

现在我们来到第三关,一点开我们就看到了一个启动广告

image-20250722112834356

前面说过全部界面都是Activity,因此这个广告界面也是Activity。

启动广告的流程:启动Activity->广告Activity->主页Activity

因此我们可以想到有两种修改的方法:

  • 修改加载时间
  • Activity切换定位,修改Intent的Activity类名称

我们可以用Activity定位来找到我们可以修改的位置,打开开发者工具,选择当前Activity

image-20250722124244153

可以找到启动广告的Activity类名为com.zj.wuaipojie.ui.AdActivity,通过类名我们可以快速定位到对应的逻辑位置

image-20250722124635821

我们先来看反编译后Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class AdActivity extends AppCompatActivity {
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ad_activity);
loadAd();
}

/* JADX INFO: Access modifiers changed from: private */
public final void jump() {
startActivity(new Intent(this, (Class<?>) ChallengeThird.class));
finish();
}

private final void loadAd() {
new Handler().postDelayed(new Runnable() { // from class: com.zj.wuaipojie.ui.AdActivity$$ExternalSyntheticLambda0
@Override // java.lang.Runnable
public final void run() {
this.f$0.jump();
}
}, 3000L);
}
}

这是一个很简单的逻辑,就是先通过onCreate启动一个实例,然后在里面使用loadAd()方法,其中设置为3000ms,然后最后使用jump()跳转到后续的Activity。

修改加载时间

现在我们要通过第一种方法:修改加载时间来完成跳启动广告的目的,打开np管理器,用同样的类搜索到广告页面的实现处,在此处,我们通过修改smali代码来实现修改加载时间

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
.method public static synthetic $r8$lambda$Ba8g8Cpz5n6fbKN5g2ojikJ3IFo(Lcom/zj/wuaipojie/ui/AdActivity;)V
.registers 1

invoke-static {p0}, Lcom/zj/wuaipojie/ui/AdActivity;->loadAd$lambda-0(Lcom/zj/wuaipojie/ui/AdActivity;)V

return-void
.end method

.method public constructor <init>()V
.registers 1

.line 10
invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

return-void
.end method

.method private final jump()V
.registers 4

.line 23
new-instance v0, Landroid/content/Intent;

move-object v1, p0

check-cast v1, Landroid/content/Context;

const-class v2, Lcom/zj/wuaipojie/ui/ChallengeThird;

invoke-direct {v0, v1, v2}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V

invoke-virtual {p0, v0}, Lcom/zj/wuaipojie/ui/AdActivity;->startActivity(Landroid/content/Intent;)V

.line 24
invoke-virtual {p0}, Lcom/zj/wuaipojie/ui/AdActivity;->finish()V

return-void
.end method

.method private final loadAd()V
.registers 5

.line 27
new-instance v0, Landroid/os/Handler;

invoke-direct {v0}, Landroid/os/Handler;-><init>()V

new-instance v1, Lcom/zj/wuaipojie/ui/AdActivity$$ExternalSyntheticLambda0;

invoke-direct {v1, p0}, Lcom/zj/wuaipojie/ui/AdActivity$$ExternalSyntheticLambda0;-><init>(Lcom/zj/wuaipojie/ui/AdActivity;)V

const-wide/16 v2, 0xbb8 //此处为我们要修改的时间位置,0xbb8转换为10进制即3000
//const-wide/16 v2, 0x0

invoke-virtual {v0, v1, v2, v3}, Landroid/os/Handler;->postDelayed(Ljava/lang/Runnable;J)Z

return-void
.end method

修改完成后我们打开app,发现开屏广告直接没有了

image-20250722125836264

修改AndroidManifest内类名(不推荐)

声明实现应用部分可视化界面的 Activity,必须使用 AndroidManifest 中的<activity>元素表示所有 Activity。系统不会识别和运行任何未进行声明的Activity。我们先来看到AndroidManifest.xml文件,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
<activity
android:name="com.zj.wuaipojie.ui.ChallengeSixth"
android:exported="false"/>
<activity
android:name="com.zj.wuaipojie.ui.ChallengeFifth"
android:exported="true"/>
<activity
android:name="com.zj.wuaipojie.ui.ChallengeFourth"
android:exported="true"/>
<activity
android:name="com.zj.wuaipojie.ui.ChallengeThird"
android:exported="false"/>
<activity
android:name="com.zj.wuaipojie.ui.ChallengeSecond"
android:exported="false"/>
<activity android:name="com.zj.wuaipojie.ui.AdActivity"/>
<activity
android:label="@string/app_name"
android:name="com.zj.wuaipojie.ui.MainActivity"
android:exported="true"><!--当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动-->
<!---指明这个activity可以以什么样的意图(intent)启动--->
<intent-filter>
<!--表示activity作为一个什么动作启动,android.intent.action.MAIN表示作为主activity启动--->
<action android:name="android.intent.action.MAIN"/>
<!--这是action元素的额外类别信息,android.intent.category.LAUNCHER表示这个activity为当前应用程序优先级最高的Activity-->
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.zj.wuaipojie.ui.ChallengeFirst"/>

因此我们可以直接把com.zj.wuaipojie.ui.AdActivity替换成我们想加载的页面即可com.zj.wuaipojie.ui.ChallengeThird

修改跳转的类

实际上这个也是跟上面一种方法一样是属于Activity切换定位,修改Intent的Activity类名称这一类方法中的。

这次我们是通过修改smali代码来实现,首先我们要找到跳转的逻辑位置,先通过com.zj.wuaipojie.ui.AdActivity找到这个类在smali代码中的全程Lcom/zj/wuaipojie/ui/AdActivity

image-20250723201304891

然后再通过这个在代码中搜索

image-20250723201402801 image-20250723201440163

我们跳转到这个代码处,先将代码转换为Java代码让我们更易看懂

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
public final class ChallengeAdapter extends Adapter<ViewHolder> {
private final List<Challenge> mChallenges;

/* renamed from: onCreateViewHolder$lambda-0 */
private static final void m0onCreateViewHolder$lambda-0(ViewHolder viewHolder, View view) {
int adapterPosition = viewHolder.getAdapterPosition();
Intent intent;
if (adapterPosition == 0) {
intent = new Intent();
intent.setClass(view.getContext(), ChallengeFirst.class);
view.getContext().startActivity(intent);
} else if (adapterPosition == 1) {
intent = new Intent();
intent.setClass(view.getContext(), ChallengeSecond.class);
view.getContext().startActivity(intent);
} else if (adapterPosition == 2) {
intent = new Intent(); //创建一个intent
intent.setClass(view.getContext(), AdActivity.class); //传入要切换Activity的类名
view.getContext().startActivity(intent); //启动对应的Activity
} else if (adapterPosition == 3) {
intent = new Intent();
intent.setClass(view.getContext(), ChallengeFourth.class);
view.getContext().startActivity(intent);
} else if (adapterPosition == 4) {
intent = new Intent();
intent.setClass(view.getContext(), ChallengeFifth.class);
view.getContext().startActivity(intent);
} else if (adapterPosition == 5) {
intent = new Intent();
intent.setClass(view.getContext(), ChallengeSixth.class);
view.getContext().startActivity(intent);
}
}

通过这个代码我们就可以看到这个切换的方法,我们通过修改他smali代码,可以实现跳过广告的效果

image-20250723202041114

我们来看看效果

image-20250723202125844

可以看到我们已经把开屏广告给干掉了。

Activity生命周期

函数名称 描述
onCreate() 一个Activity启动后第一个被调用的函数,常用来在此方法中进行Activity的一些初始化操作。例如创建View,绑定数据,注册监听,加载参数等。
onStart() 当Activity显示在屏幕上时,此方法被调用但此时还无法进行与用户的交互操作。
onResume() 这个方法在onStart()之后调用,也就是在Activity准备好与用户进行交互的时候调用,此时的Activity一定位于Activity栈顶,处于运行状态。
onPause() 这个方法是在系统准备去启动或者恢复另外一个Activity的时候调用,通常在这个方法中执行一些释放资源的方法,以及保存一些关键数据。
onStop() 这个方法是在Activity完全不可见的时候调用的。
onDestroy() 这个方法在Activity销毁之前调用,之后Activity的状态为销毁状态。
onRestart() 当Activity从停止stop状态恢进入start状态时调用状态。

弹窗定位&堆栈分析

对于弹窗广告我们有以下几种修改方法:

  • 修改xml中的versioncode(用于跳过版本检查)
  • Hook弹窗(使用算法助手Pro开启弹窗定位)
  • 修改dex弹窗代码(通过算法助手Pro日志分析堆栈找到方法名)
  • 抓包修改响应体

Hook弹窗

这里我们使用到的是算法助手Pro这个模块,选择弹窗定位

image-20250724202631917

打开后,我们注入教程demo,弹窗出现的地方,我们直接按两下返回键,即可关闭弹窗

image-20250724202743154

修改dex弹窗代码

在我们上一个方法中,我们Hook了弹窗,因此我们可以在算法助手Pro的日志中找到创建弹窗的方法名:

  • com.zj.wuaipojie.ui.ChallengeThird.onCreate
image-20250724203139902

接着我们用这个方法名,在Dex编辑中定位到对应的方法

image-20250724203941845

像这一种弹窗最后都会调用show()方法来显示出弹窗的信息,我们只要把这一段代码注释掉即可,我们这里两个弹窗都在这个方法里面,我们把两个show()方法注释掉即可

1
2
3
4
5
.line 38
#invoke-virtual {p1}, Landroidx/appcompat/app/AlertDialog$Builder;->show()Landroidx/appcompat/app/AlertDialog;

.line 53
#invoke-virtual {p1}, Lcom/zj/wuaipojie/util/CommonDialog;->show()V

重新编译签名安装,我们的弹窗就被去掉了。

布局优化

接下来我们发现,里面的横幅广告挡住了我们内容,我们想将它去掉。

我们可以用到开发助手帮助查看我们的布局

image-20250724204722732

我们点击第一个进去,找到它的id0x7f0801ca,我们使用np管理器的xml搜索功能查找资源ID(将0x去掉再搜索)。

定位到一个叫jJ.xml的文件,点击编辑进入xml文件,直接搜索id(右上角关掉id转名称)

1
2
3
4
5
6
<ImageView
android:id="@7F0801CA"
android:background="@7F0D0017"
android:layout_width="wrap_content"
android:layout_height="150.0dip"
android:layout_marginTop="100.0dip"/>

这边有两种改法:

  1. 把宽度或者高度设置为0

    1
    2
    3
    4
    5
    6
    <ImageView
    android:id="@7F0801CA"
    android:background="@7F0D0017"
    android:layout_width="0dip"
    android:layout_height="0dip"
    android:layout_marginTop="100.0dip"/>
  2. 使用visibility属性隐藏起来

    1
    2
    3
    4
    5
    6
    7
    <ImageView
    android:id="@7F0801CA"
    android:background="@7F0D0017"
    android:layout_width="wrap_content"
    android:layout_height="150.0dip"
    android:layout_marginTop="100.0dip"
    android:visibility="gone"/>

通过修改xml的方法我们可以把横幅广告隐藏起来。