某克浏览器小说阅读去广告初步

查看 97|回复 9
作者:zzzlanging   
注意:本文仅用于技术学习与研究目的,旨在分享 Android 逆向分析思路与实践。修改第三方应用程序的行为可能违反其用户服务条款。侵删
前言
由于在使用该浏览器阅读小说的过程被其不断弹出的广告所困扰,所以决定尝试改善使用该浏览器看小说的体验。主要针对的是底部的横幅广告和翻页过程中出现的广告。
定位
使用开发者助手查看广告控件的属性以及其值

然后使用Jadx查看App的源码,在资源文件中搜索这个控件的id

这里选择第一个名称看起来最符合广告的布局文件,查看它的引用,R文件相关的不管,直接去看使用的类

package com.noah.sdk.business.render.container;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import com.noah.api.DownloadApkInfo;
import com.noah.api.SdkRenderRequestInfo;
import com.noah.api.bean.TemplateStyleBean;
import com.noah.logger.util.RunLog;
import com.noah.sdk.business.render.g;
import com.noah.sdk.business.render.template.e;
/* compiled from: ProGuard */
/* loaded from: classes5.dex */
public class c extends b {
    private static final String TAG = "SdkInsideTemplateStyle";
    private TemplateStyleBean aMP;
    private boolean aMQ;
    private int aMd;
    public c(TemplateStyleBean templateStyleBean, boolean z) {
        this.aMQ = z;
        this.aMd = templateStyleBean.getTemplateId();
        this.aMP = templateStyleBean;
    }
    @Override // com.noah.sdk.business.render.container.b
    protected int T(Context context) {
        int i12 = this.aMd;
        String str = "noah_sdk_template_native_ad_layout";
        if (i12 != 1) {
            if (i12 == 3) {
                str = this.aMQ ? "noah_sdk_template_banner_apk_layout" : "noah_sdk_template_banner_ad_layout";
            } else if (i12 == 16) {
                str = "noah_sdk_template_mt_banner_ad_layout";
            } else if (i12 == 17) {
                str = "noah_sdk_template_three_combine_layout";
            } else if (i12 == 5) {
                str = "noah_sdk_template_native_app_info_ad_layout";
            } else if (i12 == 6) {
                str = "noah_sdk_template_banner_three_ad_layout";
            } else if (i12 == 9) {
                str = "noah_sdk_template_native_live_layout";
            } else if (i12 == 10) {
                str = "noah_sdk_template_native_bubble_layout";
            } else if (i12 == 11) {
                str = "noah_sdk_template_native_ad_tv1_layout";
            } else if (i12 == 12) {
                str = "noah_sdk_template_native_ad_tv2_layout";
            } else if (i12 == 13) {
                str = "noah_sdk_template_native_live_tv_layout";
            } else if (i12 == 14) {
                str = "noah_sdk_template_banner_live_layout";
            } else if (i12 == 15) {
                str = "noah_sdk_template_rect_shape";
            }
        }
        RunLog.d(TAG, "使用渲染模版 template id :".concat(str), new Object[0]);
        return g.fI(str);
    }
    @Override // com.noah.sdk.business.render.container.b
    public com.noah.sdk.business.render.a a(SdkRenderRequestInfo sdkRenderRequestInfo, DownloadApkInfo downloadApkInfo) {
        return new e(this, sdkRenderRequestInfo, downloadApkInfo);
    }
    @Override // com.noah.api.delegate.ISdkTemplateContainer
    public int getTemplateId() {
        return this.aMd;
    }
    @Override // com.noah.api.delegate.ISdkTemplateContainer
    public TemplateStyleBean getTemplateStyleBean() {
        return this.aMP;
    }
    @Override // com.noah.api.delegate.ISdkTemplateContainer
    public View getTemplateView(Context context) {
        return com.noah.sdk.business.render.e.xk().openLayoutInflater(context).inflate(T(context), (ViewGroup) null);
    }
}
这里有几个关键方法:
protected int T(Context context),根据构造函数传入的模板ID,从多个布局文件(也就是我们之前搜索到的)选择一个具体要使用的布局文件名,然后把布局文件名转成真正的资源ID。
public View getTemplateView(Context context),它调用了安卓的 inflate 方法,把上一步 T(context) 方法选择的那个 XML 布局文件,真正地转换成了一个可以显示在屏幕上的 View 对象。
我们这里可以hook getTemplateView让它返回空,这样就不会对广告进行渲染。
Hook 广告渲染
/**
* 目标: 在广告视图被创建的瞬间进行拦截,阻止其返回。
* 策略: Hook com.noah.sdk.business.render.container.c 类的 getTemplateView 方法。
*/
console.log("
  • 最终广告拦截脚本已加载...");
    Java.perform(() => {
        console.log("
  • Java 环境就绪,准备 Hook 广告视图工厂...");
        try {
            const CLASS_TO_HOOK = "com.noah.sdk.business.render.container.c";
            const AdContainer = Java.use(CLASS_TO_HOOK);
            // Hook getTemplateView(Context context) 方法
            AdContainer.getTemplateView.overload('android.content.Context').implementation = function(context) {
                console.log(`[!!!] 成功拦截到广告视图创建调用: ${CLASS_TO_HOOK}.getTemplateView()`);
                // 我们可以通过调用 this.getTemplateId() 来获取当前准备加载的广告模板ID,用于调试
                const templateId = this.getTemplateId();
                console.log(`
  • 准备加载的广告模板 ID 为: ${templateId}`);
                // === 核心拦截操作 ===
                // 策略一 (推荐): 直接返回 null,让广告视图创建失败。
                console.log("
  • 已阻止广告视图创建,返回 null。");
                return null;
            };
            console.log(`[+] 已成功部署对 ${CLASS_TO_HOOK}.getTemplateView 的 Hook。`);
        } catch (error) {
            console.error(`[!] 最终 Hook 失败: ${error.message}`);
            console.error("[!] 请确认类名和方法签名是否完全正确。");
        }
    });

    hook之后底部广告和广告页的广告都不在渲染,但是还有夸克自带的“开会员免广告”这个广告,同样使用开发者助手和Jadx进行定位即可(过程同上,这里就不多写了),直接给代码
    /**
    * Frida 终极组合拦截脚本
    * 目标: 彻底去除夸克小说中的所有翻页广告(包括第三方SDK广告和App自有备用广告)。
    * 策略: 同时 Hook 两个关键入口点,形成双重保险。
    */
    console.log("
  • 终极组合广告拦截脚本已加载...");
    Java.perform(() => {
        console.log("
  • Java 环境就绪,准备部署多点 Hook...");
        // --- 防线 1: 禁用第三方广告SDK (Noah SDK) 的顶层服务入口 ---
        try {
            const SdkService = Java.use("com.noah.sdk.business.render.container.c");
            SdkService.getTemplateView.overload('android.content.Context').implementation = function(reqInfo) {
                console.log("[!!!] 防线1: 已拦截第三方广告SDK入口 (getTemplateView)。返回 null。");
                return null;
            };
            console.log("[+] 防线1部署成功");
        } catch (error) {
            console.error(`[!] 防线1部署失败: ${error.message}`);
        }
        // --- 防线 2: 禁用App自身的广告页面渲染入口 ---
        try {
            const AdPageView = Java.use("com.uc.application.novel.ad.view.NovelMixedAdPageView");
            AdPageView.drawPageData.overload('com.uc.application.novel.reader.NovelPage').implementation = function(novelPage) {
                console.log("[!!!] 防线2: 已拦截App广告页面渲染入口 (drawPageData)。");
                // 直接返回,不执行任何绘制操作
                return;
            };
            console.log("[+] 防线2部署成功");
        } catch (error) {
            console.error(`[!] 防线2部署失败: ${error.message}`);
        }
    });
    Xposed模块开发
    考虑到去广告需要长久hook,所以这里将frida脚本改写成一个Xposed模块。该Xposed模块我用的是kotlin语言编写,hook的点是com.noah.sdk.business.render.container.c的上层引用中动态渲染广告的函数(均能实现相应效果)。
    1. 创建 Xposed 模块项目
    使用 Android Studio 创建一个标准的 "Empty Views Activity" 项目,并进行如下配置:

  • settings.gradle.kts: 添加 Xposed 官方仓库地址。
    dependencyResolutionManagement {
      repositories {
          google()
          mavenCentral()
          maven { url = uri("https://api.xposed.info/") }
      }
    }

  • build.gradle.kts: 添加 Xposed API 依赖。
    dependencies {
      compileOnly("de.robv.android.xposed:api:82")
      compileOnly("de.robv.android.xposed:api:82:sources")
    }

  • AndroidManifest.xml: 声明这是一个 Xposed 模块。
      
      
      

    2. 编写 Hook 核心逻辑
    我们将 Frida 的“双重防御”策略,用 Xposed API 的语法“翻译”过来。创建一个 HookEntry.kt 作为模块入口:
    package com.example.quarkadblocker
    import de.robv.android.xposed.IXposedHookLoadPackage
    import de.robv.android.xposed.XC_MethodHook
    import de.robv.android.xposed.XposedBridge
    import de.robv.android.xposed.XposedHelpers
    import de.robv.android.xposed.callbacks.XC_LoadPackage
    class HookEntry : IXposedHookLoadPackage {
        private val TARGET_PACKAGE_NAME = "com.quark.browser"
        override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
            if (lpparam.packageName != TARGET_PACKAGE_NAME) {
                return
            }
            XposedBridge.log("[QuarkAdBlocker] 已成功注入夸克浏览器进程...")
            // --- 防线 1: 禁用第三方广告SDK (Noah SDK) 的顶层服务入口 ---
            try {
                XposedHelpers.findAndHookMethod(
                    "com.noah.sdk.business.render.DynamicRenderService",
                    lpparam.classLoader,
                    "getNativeRender",
                    "com.noah.api.SdkRenderRequestInfo",
                    object : XC_MethodHook() {
                        override fun beforeHookedMethod(param: MethodHookParam) {
                            XposedBridge.log("[QuarkAdBlocker] 防线1: 已拦截 getNativeRender。")
                            param.result = null // 直接返回 null,阻止渲染器创建
                        }
                    }
                )
                XposedBridge.log("[QuarkAdBlocker] 防线1部署成功!")
            } catch (e: Throwable) {
                XposedBridge.log("[QuarkAdBlocker] 防线1部署失败: ${e.message}")
            }
            // --- 防线 2: 禁用App自身的广告页面渲染入口 ---
            try {
                XposedHelpers.findAndHookMethod(
                    "com.uc.application.novel.ad.view.NovelMixedAdPageView",
                    lpparam.classLoader,
                    "drawPageData",
                    "com.uc.application.novel.reader.NovelPage",
                    object : XC_MethodHook() {
                        override fun beforeHookedMethod(param: MethodHookParam) {
                            XposedBridge.log("[QuarkAdBlocker] 防线2: 已拦截 drawPageData。")
                            param.result = null // 阻止页面渲染任何内容
                        }
                    }
                )
                XposedBridge.log("[QuarkAdBlocker] 防线2部署成功!")
            } catch (e: Throwable) {
                XposedBridge.log("[QuarkAdBlocker] 防线2部署失败: ${e.message}")
            }
        }
    }
    最后,在 assets/xposed_init 文件中指定这个入口类。
    3. 部署与激活
    [ol]
  • 构建 APK: 在 Android Studio 中 Build -> Build APK(s)。
  • 安装 LSPatch: 从官方渠道下载并安装 LSPatch Manager。
  • 修补应用: 使用 LSPatch 的“便携模式”修补夸克浏览器,生成一个新的 APK。
  • 安装与激活:
  • 卸载原版夸克,安装修补版。
  • 安装自己构建的 QuarkAdBlocker.apk 模块。
  • 在 LSPatch Manager 中为夸克浏览器启用我们的模块。
  • 重启生效: 强制停止并重启夸克浏览器。
    [/ol]
    最终效果




    广告, 夸克

  • jianfengjx   

    伸手党坐等成品
    zzzlanging
    OP
      

    图片上传到图床的,最后几张缩放没搞好
    Cxcj   

    hahaha这个好
    1haoren88   

    图片好大 正常吗? 还是先支持一下
    Beeceelee   

    比较强的,实用工具
    jhwwan   

    很实用的工具,谢谢分享
    就是今天   

    谢谢分享
    Luxanna   

    感谢分享!
    iancr7   

    不错,谢谢分享,
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部