Android 第三方應用廣告攔截實現(xiàn)


前年在的上一家公司纯露,制造的機器里應用裝有不良廣告,嚴重影響了兒童客戶使用者的思想健康代芜,導致被人投訴埠褪。于是乎,就有了想研發(fā)一款類似于360廣告屏蔽的應用的念頭。嗯钞速,事情就是這樣贷掖,現(xiàn)在切入主題。

目前市場上有很多安全軟件玉工,它們攔截第三方應用廣告的方式都不一樣羽资,比如說有 以so 注入方式來攔截彈出廣告
現(xiàn)在我們來看下這種方式的詳細情況:

要做到攔截遵班,首先我們得知道廣告是怎么出來的屠升,原來第三方應用大部分是以加入廣告jar形式加入廣告插件,然后在AndroidManifest中聲明廣告service或者在程序中執(zhí)行廣告Api狭郑,廣告插件再通過Http請求去加載廣告腹暖。在java中,有四種訪問網(wǎng)絡的接口翰萨,如apache的http庫(如下介紹)脏答,這幾種方式首先都會通過getaddrinfo函數(shù)獲取域名地址,然后通過connect函數(shù)連接到服務器讀取廣告信息亩鬼。

  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)等加載網(wǎng)頁玷过。
  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)方法執(zhí)行訪問的網(wǎng)絡的動作辛蚊。
  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方法連接網(wǎng)絡。
  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方法連接網(wǎng)絡初澎。

然后再來說說動態(tài)庫注入,具體什么是動態(tài)庫注入虑凛,以及如何注入碑宴,網(wǎng)上有很多文章,這里就不介紹卧檐。動態(tài)庫注入攔截呢墓懂,主要是攔截getaddrinfo,根據(jù)條件返回錯誤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方法連接網(wǎng)絡霉囚。攔截網(wǎng)絡請求,達到攔截作用匕积。不過需要注意一點就是攔截之前要確定你所攔截的動態(tài)庫是否是你需要攔截的庫盈罐?例如A程序調(diào)用了動態(tài)庫BO和CO榜跌,而BO和CO都調(diào)用了connect函數(shù),此時需要攔截BO的請求盅粪,需要注入到BO動態(tài)庫并修改GOT表钓葫,而不是注入到CO中。

攔截HTTP方式廣告在多數(shù)廣告包中票顾,應用程序首先會通過apache的http庫或JDK中的http方法先將廣告數(shù)據(jù)下載過來础浮,然后通過WebView顯示。這種方式通過注入攔截進程的/system/lib/libjavacore.so可實現(xiàn)廣告地址攔截奠骄。
攔截WebView方式廣告廣告插件也可以直接通過WebView加載URL豆同,通過分析WebView加載流程可知它的網(wǎng)絡處理過程均交給libchromium_net庫來完成。因此通過注入libjavacore.so是無法實現(xiàn)攔截含鳞,而是需要注入到/system/lib/libchromium_net.so影锈。

通過這種方式已經(jīng)完全能夠攔截掉第三方APP廣告,但存在一些問題:
1.廣告商可以通過JNI方式調(diào)用系統(tǒng)getaddrinfo與connect實現(xiàn)自己的解析與連接過程的動態(tài)庫蝉绷,從而跳過libjavacore.so導致攔截無效鸭廷。 2.攔截WebView方式廣告雖然能夠不顯示廣告,但通常仍然會有浮動框顯示”網(wǎng)頁無法打開”熔吗,從而影響美觀辆床。 3.最重要的是我們機器是沒有root權限的!磁滚!
第三個問題直接導致了放棄了這種注入做法佛吓。 來來去去一段時間后,目前是采用android 系統(tǒng)本地掃描第三方應用廣告形式垂攘。具體怎么做维雇,請往下看!

如果對這種方式不了解的話晒他,建議先看下這篇 Android系統(tǒng)掃描帶廣告應用的做法吱型。

所以具體廣告插件掃描方案是匹配包名+類名形式的: 1.掃描本地所有第三方應用,列出一個應用中的所有類陨仅,將包名+類名方式與廣告插件特征庫進行匹配津滞; 2.將匹配出來的應用所帶廣告特征,通過系統(tǒng)提供傳入接口灼伤,將這些規(guī)則設置進去触徐。(當然,系統(tǒng)代碼是需要改的狐赡,做了一些處理撞鹉,主要是在上面介紹中的幾種訪問網(wǎng)絡方式上做了判斷處理)
這種方案的關鍵在于廣告特征庫的完善,廣告插件特征庫收集越全鸟雏,掃描出來的廣告插件就可以越準確享郊。所幸,公司有幾位大神孝鹊,做過類似的事情炊琉,所以工作簡單了多些。
獲取第三方應用:

/** * 查詢機器內(nèi)非本公司應用 */ 
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); 
           //屏蔽掉公司內(nèi)部應用
           //...
           //判斷是否為非系統(tǒng)預裝的應用程序 
           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 需要傳給系統(tǒng)的一個規(guī)則特征目锭。

匹配出所有應用的所屬規(guī)則特征后评汰,接下來需要傳給系統(tǒng)了,系統(tǒng)將滿足需求的幾個接口提供出來痢虹。這邊涉及到修改系統(tǒng)層代碼被去,我就主要講下實現(xiàn)思路,會貼出關鍵的幾個代碼奖唯。 實現(xiàn)思路:系統(tǒng)根據(jù)應用層傳入的應用包名以及規(guī)則惨缆,將其緩存,在webview或http處請求時丰捷,對其進行判斷處理坯墨。
添加某應用規(guī)則接

/** * 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;
}

由于系統(tǒng)代碼這部分的改動并非是我改的,更深細節(jié)處的理論就不清楚了病往。 應用層的廣告特征庫為了可以持續(xù)更新捣染,建議可以做成網(wǎng)絡更新方式。 據(jù)此停巷,廣告攔截功能實現(xiàn)就完成了耍攘,可能會有瑕疵累贤,不過持續(xù)優(yōu)化中。

如果覺得此文不錯少漆,麻煩幫我點下“喜歡”。么么噠硼被!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末示损,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嚷硫,更是在濱河造成了極大的恐慌检访,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仔掸,死亡現(xiàn)場離奇詭異脆贵,居然都是意外死亡,警方通過查閱死者的電腦和手機起暮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門卖氨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人负懦,你說我怎么就攤上這事筒捺。” “怎么了纸厉?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵系吭,是天一觀的道長。 經(jīng)常有香客問我颗品,道長肯尺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任躯枢,我火速辦了婚禮则吟,結果婚禮上,老公的妹妹穿的比我還像新娘锄蹂。我一直安慰自己逾滥,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布败匹。 她就那樣靜靜地躺著寨昙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀亩。 梳的紋絲不亂的頭發(fā)上舔哪,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音槽棍,去河邊找鬼捉蚤。 笑死抬驴,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的缆巧。 我是一名探鬼主播布持,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陕悬!你這毒婦竟也來了题暖?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤捉超,失蹤者是張志新(化名)和其女友劉穎胧卤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拼岳,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡枝誊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惜纸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叶撒。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耐版,靈堂內(nèi)的尸體忽然破棺而出痊乾,到底是詐尸還是另有隱情,我是刑警寧澤椭更,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布哪审,位于F島的核電站,受9級特大地震影響虑瀑,放射性物質發(fā)生泄漏湿滓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一舌狗、第九天 我趴在偏房一處隱蔽的房頂上張望叽奥。 院中可真熱鬧,春花似錦痛侍、人聲如沸朝氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赵哲。三九已至,卻和暖如春君丁,著一層夾襖步出監(jiān)牢的瞬間枫夺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工绘闷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留橡庞,地道東北人较坛。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像扒最,于是被迫代替她去往敵國和親丑勤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 鏈接:http://www.reibang.com/p/fd61e8f4049e 一吧趣、簡介 這部分主要介紹下 W...
    柒黍閱讀 1,781評論 0 4
  • 這篇博客主要來介紹 WebView 的相關使用方法再菊,常見的幾個漏洞,開發(fā)中可能遇到的坑和最后解決相應漏洞的源碼颜曾,以...
    Shawn_Dut閱讀 7,209評論 3 55
  • AndroidWebView 一纠拔、簡介 WebView在Android平臺上是一個特殊的View, 基于webki...
    斌林誠上閱讀 1,867評論 0 5
  • 測試APP權限overview靜態(tài)分析Android 權限定制權限動態(tài)分析測試自定義url靜態(tài)分析動態(tài)分析測試通過...
    Ireliaaa閱讀 922評論 0 1
  • 郭老板聞言立刻極為熱情的說泛豪,“杜老板您說多少就是多少稠诲,我絕不還口!” “您太抬舉我了……還是看看這梳子和我的朋友有...
    花妖兒閱讀 251評論 0 1