Android 第三方应用 广告拦截功能实现

前段时间,公司制造的机器里应用装有不良广告,严重影响了儿童客户使用者的思想健康,导致被人投诉。于是乎,就有了想研发一款类似于360广告屏蔽的应用的念头。嗯,事情就是这样,现在切入主题。

目前市场上有很多安全软件,它们拦截第三方应用广告的方式都不一样,比如说有 以so 注入方式来拦截弹出广告

现在我们来看下这种方式的详细情况:

要做到拦截,首先我们得知道广告是怎么出来的,原来第三方应用大部分是以加入广告jar形式加入广告插件,然后在AndroidManifest中声明广告service或者在程序中执行广告Api,广告插件再通过Http请求去加载广告。在java中,有四种访问网络的接口,如apache的http库(如下介绍),这几种方式首先都会通过getaddrinfo函数获取域名地址,然后通过connect函数连接到服务器读取广告信息。

  1. WebView(源码文件在frameworks/base/core/java/android/webkit/WebView.java)。通过WebView类的void loadUrl(String url)、void postUrl(String url, byte[] postData)、void loadData(String data, String mimeType, String encoding)、void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)、void evaluateJavascript(String script, ValueCallback resultCallback)等加载网页。
  2. apache-http(源码目录在external/apache-http/ , HttpGet 和 HttpPost类)。通过external/apache-http/src/org/apache/http/impl/client/DefaultRequestDirector.java中的DefaultRequestDirector类的HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context)方法执行访问的网络的动作。
  3. okhttp(源码目录在external/okhttp/)。通过external/okhttp/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java中的HttpEngine类的private void connect(Request request) throws IOException方法连接网络。
  4. URL(源码在libcore/luni/src/main/java/java/net/URL.java)。通过libcore/luni/src/main/java/java/net/URL.java中的URL类的URLConnection openConnection() throws IOException方法和URLConnection openConnection(Proxy proxy) throws IOException方法连接网络。

然后再来说说动态库注入,具体什么是动态库注入,以及如何注入,网上有很多文章,这里就不介绍。动态库注入拦截呢,主要是拦截getaddrinfo,根据条件返回错误来拦截网络请求,达到拦截作用。不过需要注意一点就是拦截之前要确定你所拦截的动态库是否是你需要拦截的库?例如A程序调用了动态库BO和CO,而BO和CO都调用了connect函数,此时需要拦截BO的请求,需要注入到BO动态库并修改GOT表,而不是注入到CO中。

拦截HTTP方式广告在多数广告包中,应用程序首先会通过apache的http库或JDK中的http方法先将广告数据下载过来,然后通过WebView显示。这种方式通过注入拦截进程的/system/lib/libjavacore.so可实现广告地址拦截。

拦截WebView方式广告广告插件也可以直接通过WebView加载URL,通过分析WebView加载流程可知它的网络处理过程均交给libchromium_net库来完成。因此通过注入libjavacore.so是无法实现拦截,而是需要注入到/system/lib/libchromium_net.so。

通过这种方式已经完全能够拦截掉第三方APP广告,但存在一些问题:

1.广告商可以通过JNI方式调用系统getaddrinfo与connect实现自己的解析与连接过程的动态库,从而跳过libjavacore.so导致拦截无效。

2.拦截WebView方式广告虽然能够不显示广告,但通常仍然会有浮动框显示”网页无法打开”,从而影响美观。

3.最重要的是我们机器是没有root权限的!!

第三个问题直接导致了放弃了这种注入做法。

来来去去一段时间后,目前是采用android 系统本地扫描第三方应用广告形式。具体怎么做,请往下看!

如果对这种方式不了解的话,建议先看下这篇 Android系统扫描带广告应用的做法

所以具体广告插件扫描方案是匹配包名+类名形式的:

1.扫描本地所有第三方应用,列出一个应用中的所有类,将包名+类名方式与广告插件特征库进行匹配

2.将匹配出来的应用所带广告特征,通过系统提供传入接口,将这些规则设置进去。(当然,系统代码是需要改的,做了一些处理,主要是在上面介绍中的几种访问网络方式上做了判断处理)

这种方案的关键在于广告特征库的完善,广告插件特征库收集越全,扫描出来的广告插件就可以越准确。所幸,公司有几位大神,做过类似的事情,所以工作简单了多些。

获取第三方应用:

   /**
     * 查询机器内非本公司应用
     */
    public List<PackageInfo> getAllLocalInstalledApps() {
        List<PackageInfo> apps = new ArrayList<PackageInfo>();
        if(pManager == null){
            return apps;
        }
        //获取所有应用
        List<PackageInfo> paklist = pManager.getInstalledPackages(0);
        for (int i = 0; i < paklist.size(); i++) {
            PackageInfo pak = (PackageInfo) paklist.get(i);

           //屏蔽掉公司内部应用
           //...

           //判断是否为非系统预装的应用程序
            if ((pak.applicationInfo.flags & pak.applicationInfo.FLAG_SYSTEM) <= 0) {
                // customs applications
                apps.add(pak);
            }
        }
        return apps;
    }

获取某个应用的广告特征:

public static List<String> getClassNameByDex(Context context,
            String packageName) {

        List<String> datalist = new ArrayList<String>();
        String path = null;
        try {
            path = context.getPackageManager().getApplicationInfo(packageName,
                    0).sourceDir;// 获得某个程序的APK路径
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        try {
            if(TextUtils.isEmpty(path)){
                return datalist;
            }
            DexFile dexFile = new DexFile(path);// get dex file of APK
            Enumeration<String> entries = dexFile.entries();
            while (entries.hasMoreElements()) {// travel all classes
                String className = (String) entries.nextElement();
                String totalname = packageName + "."+className;
                datalist.add(totalname);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return datalist;
    }

将应用中的所有类名与特征库进行匹配:

for (PackageInfo info : infolsit) {
        if (info == null) {
            continue;
        }
        data = getClassNameByDex(context,info.packageName);
        if(data == null){
            Log.d(TAG,"getAdFlagForLocalApp()  类名解析出错"+info.packageName);
            continue;
        }
        sgPgmap = new HashMap<String, String>();
        for (String clsname : data) {
            for (ADSInfo adinfo : flaglist) {
                String flag = adinfo.getAdFlag();  //广告样本库的某一标识
                String adpg = adinfo.getAdName();  //广告样本库的某一包名
                if (clsname.contains(adpg)) {  //匹配类名与广告特征库里的匹配符,看是否包含关系
                    sgPgmap.put(flag,info.packageName);
                }
            }
        }
        if(sgPgmap.size() > 0){
            //AdsPgInfo  一个对应应用里包含了多少个标识
            adspginfo = new AdsPgInfo(info.packageName, sgPgmap);
            pglist.add(adspginfo);
        }
    }

ps: 在匹配时,有一个很注意的点,有时候单单类名匹配不准,或者会漏掉某些广告,所以应该加上包名,再去匹配特征库里的匹配符,这样才能百无一漏。

在此举例一个指智广告的特征(特征显示形式可自定义,只要符合自己的解析策略即可):

ads.banner.zhidian#指智广告#com/adzhidian/#ad.zhidian3g.cn
  • ads.banner.zhidian 为该类型广告标识,主要是为了匹配时应用对应标识的简洁性,不用直接跟着一群特征到处跑。。
  • 指智广告 该广告名称
  • com/adzhidian/ 该广告用来匹配应用中类名的匹配符,当应用中某一(包名+类名)包含该匹配符时,说明了该应用包含该广告
  • ad.zhidian3g.cn 需要传给系统的一个规则特征。

匹配出所有应用的所属规则特征后,接下来需要传给系统了,系统将满足需求的几个接口提供出来。这边涉及到修改系统层代码,我就主要讲下实现思路,会贴出关键的几个代码。

实现思路:系统根据应用层传入的应用包名以及规则,将其缓存,在webview或http处请求时,对其进行判断处理。

添加某应用规则接口:

/**
 * add Adblock url of package pkgName
 */
 private boolean addAdblockUrlInner(String pkgName, String url) {
    synchronized (mAdblockEntries) {
      HashMap<String, UrlEntry> pkgEntry = mAdblockEntries.get(pkgName);
     if (pkgEntry == null) {
        pkgEntry = new HashMap<String, UrlEntry>();
        if (pkgEntry == null) {
            Slog.e(TAG, "addAdblockUrl():new HashMap<String, UrlEntry>() fail!");
            return false;
        }
        mAdblockEntries.put(pkgName, pkgEntry);
     }
     UrlEntry entry = pkgEntry.get(url);
     if (entry == null) {
        pkgEntry.put(url, new UrlEntry(0, false));
      } else {
        entry.deleted = false;
      }
   }
   return true;
}

WebView类postUrl处判断处理:

/**
 * Loads the given URL.
 *
 * @param url the URL of the resource to load
 */
   public void loadUrl(String url) {
        checkThread();
        if (!isAddressable(url)) {
           return;
        }
       if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl=" + url);
       if(!isChromium && url.startsWith("file://")){
       Log.e("WebView.java", "loadurl setLocalSWFMode");
       mProvider.setLocalSWFMode();
   }

 /**
  * Returns true if the url is not included by adblock service
  */
 private boolean isAddressable(String url) {
     boolean addressable = true;
     AdblockManager adblockManager = AdblockManager.getInstance();
     if (adblockManager != null) {
       String adblockUrl =  adblockManager.containedAdblockUrl(ActivityThread.currentPackageName(), url);
     if (adblockUrl != null) {
         addressable = false;
         adblockManager.increaseNumberOfTimes(ActivityThread.currentPackageName(), adblockUrl);
      }
      }
      return addressable;
  }

由于系统代码这部分的改动并非是我改的,更深细节处的理论就不清楚了。

应用层的广告特征库为了可以持续更新,建议可以做成网络更新方式。

据此,广告拦截功能实现就完成了,可能会有瑕疵,不过持续优化中。 有大神如果有更好的拦截实现跟策略,请您麻烦私信我,让我好好请教,非常感谢。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 06:25:45

Android 第三方应用 广告拦截功能实现的相关文章

Android 第三方应用广告拦截实现

前段时间,公司制造的机器里应用装有不良广告,严重影响了儿童客户使用者的思想健康,导致被人投诉.于是乎,就有了想研发一款类似于360广告屏蔽的应用的念头.嗯,事情就是这样,现在切入主题. 目前市场上有很多安全软件,它们拦截第三方应用广告的方式都不一样,比如说有 以so 注入方式来拦截弹出广告. 现在我们来看下这种方式的详细情况: 要做到拦截,首先我们得知道广告是怎么出来的,原来第三方应用大部分是以加入广告jar形式加入广告插件,然后在AndroidManifest中声明广告service或者在程序

android四大组件之BroadcastReceiver短信拦截功能

首先我们要配置一些清单文件 <receiver android:name="com.example.smslinser.MyReceiver" >            <intent-filter android:priority="1000">                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>   

Android应用盈利广告平台的嵌入方法详解

一.如何学习Android   android开发(这里不提platform和底层驱动)你需要对Java有个良好的基础,一般我们用Eclipse作为开发工具.对于过多的具体知识详细介绍我这里不展开,我只说我个人的学习方法和本人以为的好的步骤.   1.搭建环境:包括对Java和Eclipse及模拟器(有真机再好不过)的熟悉,对于android的tools和adb命令可以放到后面熟悉了解.   2.从helloworld开始:了解一个基本的android应用代码包含哪些,各部分做什么用.(这里只要

专业广告拦截工具AdGuard 2.0.6.599 Mac中文版

Adguard for Mac中文版作为世界上第一款专为macOS设计的独立广告拦截程序,它通过阻止广告和其他恶意内容为您提供不间断且流畅的网页浏览体验,而不会对Mac资源造成损失,而且Adguard还会密切关注您的浏览安全性以及您的隐私保护,事实由其强大的间谍软件过滤器支持,阻止跟踪工具,危险请求和其他恶意内容,最终可能会危害您的隐私或伤害你的Mac.另外Adguard作为Mac上的独立应用程序提供,因此,您可以使用多个浏览器,而无需安装作为每个浏览器的扩展程序的广告拦截器. Adguard

可过滤多种广告的“ADM(阿呆喵)广告拦截工具”V1.8.0.8下载

网络上的广告有时让人目不暇接,观看视频时也会有一段广告.为了去除这些广告,可以使用一些过滤规则.广告屏蔽插件等.本文提供的这个工具为卡饭论坛网友“Tick90011”制作,可以高效过滤多种广告,经过一段时间的试用,感觉效果还不错,推荐大家一试. 官方网站下载: http://www.admflt.com/ 更新日志: 性能优化 [*]优化匹配速度, 优化菜单 [*]增强窗口拦截功能, 比如之前音乐盒弹窗无法拦截 [*]优化升级模块 细节优化 [*][email protected] 语法 * 从

android闹钟小案例之功能阐述

最近花了一个星期左右的时间做了一个小闹钟,现在回过头来进行总结下.刚开始接触这个案例,总体觉着没有太大的难度,都是一些基础知识的堆砌,可现实总比理想残酷啊,这几天下来,每天都会有意料之外的bug出现.在debug的过程中,深深体会到了百度一下和谷歌谷歌的用处,基本常见的问题都可以在这里得到提示甚至答案.这篇文章先来介绍下这个案例所实现的功能,接下来再用几遍文章记录所用到的知识点. 功能简介: 1.闹钟基本功能:和平时的闹钟一样,用户在界面选择闹钟响起的时间,到达所设定时间后执行相应的指定操作.

Android 第三方类库简单使用之EventBus

Android 第三方类库之EventBus 1 PS 工欲善其事必先利其器. Eventbus也是一款在开发中常用的利器 这篇也对EventBus的简单介绍和使用,与之前个xutils介绍的级别一样.http://www.cnblogs.com/greentomlee/p/6025470.html 作者:修雨轩陈 @博客园 2 简介 开源项目地址: https://github.com/greenrobot/EventBus EventBus是一款针对Android优化的发布/订阅事件总线.主

iOS9的广告屏蔽功能在谷歌背后捅了致命一刀?

苹果此前就曾以隐私保护和用户偏好的名义提醒过开发人员,它们将会在即将到来的iOS9中增加广告屏蔽的功能.这预期最早将会随着九月九号的新iPhone一起到来(事实上推迟了).这在谷歌,移动应用开发公司,和应用发行商之中制造了一些紧张的气氛. 如果iOS9和广告屏蔽功能被广泛采用的话,这就意味着700亿美元的移动广告市场将会遭受到严重的颠覆.广告屏蔽越多,在手游和移动应用中可以被用户看到的广告就越少.应用发行商,广告平台,以及营销公司的收入就会越少.手游公司现在还不需要恐慌,但是大家最好打醒十二分精

JS~模拟表单在新窗口打开,避免广告拦截

说起广告拦截,这应该是浏览器的一个特性,它会将window.open产生的窗口默认为一个广告,将它进行拦截,但有时,这不是我们所希望的,有时,我们就是需要它在客户端的浏览器上弹出一个新窗口,以展示数据处理的更新结果,例如,一个创建商品的预览功能,它需要先保存数据,然后再在新窗口展示最新的信息,这种需求并不少,而大多数人的作法就是使用window.open去弹窗口,但它确实不是一种好的方式! 新方式来了 我们知道表单提交实际上可以把POST的结果响应到新窗口上,我们就是利用表单的这种性质,在JS中