文章目录
  1. 1. 拦截思路
    1. 1.1. 修改 hosts
    2. 1.2. 注入系统服务,屏蔽广告 view
  2. 2. 实现
    1. 2.1. root

上一篇说到怎么检测一个应用是否带有广告。那么现在就来说说怎么拦截广告。在开始之前首先感谢看雪论坛的牛人的分享,后面的方法基本是借鉴别人的想法的。

拦截思路

修改 hosts

以前在 PC 通过修改 hosts 来访问 google 的哥们对这个东西应该不陌生吧。同样在 android 也有这个东西。在 /system/etc/hosts 然后整个 /system/etc 链接到了根目录的 /etc 。

有了这个文件,网络请求就优先去读这个文件中的配置,如果 hosts 中有要访问的域名,就不会去请求 DNS 解析,所以如果把那些广告的域名写到这个文件里面,然后设置成 127.0.0.1 的话,那些广告 sdk 就无法从广告服务器获取广告数据了,就无法展现出来了。例如这样(下面这2个是 google ad 的):

127.0.0.1 adwords.google.com
127.0.0.1 adwords.google.sk

然后这样设置的话,一般那些广告 sdk 访问网络都会报一个 xx hosts 无法解析的网络异常错误。

前面看雪论坛的哥们分析了乐安全是用这种方式的,我研究了下腾讯的手机管家,也是用这种方式。腾讯管家,是你勾选了那些应用,那就对应把这几个应用使用的广告 sdk 的广告域名写道 /system/etc/hosts 中。每次设置完后,去看 hosts 文件的更新日期就发现被改变了。

这种方法需要 root 权限,因为 system 分区是只读的,写 hosts 之前得先 remount 成读写的,而且 remount 成读写的之后,写也需要 root 权限。

这种方法比较简单。能够阻止广告 sdk 去服务器取数据,不仅省去广告的骚扰,而且能够节省广告的流量开销(当然在 wifi only 的平板上可以忽略这点)。但是同时广告 sdk 方也很容易应对。 hosts 文件只能记录对应一个域名,广告 sdk 只要定期随机更换域名去取广告数据就能够避免被屏蔽了(好像多盟广告就已经这么干了)。

注入系统服务,屏蔽广告 view

这个属于比较高级(麻烦)的做法。具体的去看前面贴出那个看雪论坛的哥们分析的金山手机毒霸的帖子吧。简单来说就是向 system server 注入自己的代码,然后在 view 处理流程中检测出 activity 中的广告 view (一般的广告都有一个 view 作为载体的),然后把这个 view 屏蔽掉(设置为 GONE)。

这个也是需要 root 的(进程注入的 api 正常是用来调试用的,只允许注入子进程,但是 root 没有这个限制)。然后实现起来很麻烦。而且会影响正常的程序的效率。在 view 布局的流程加了一道这种工序。而且可能会影响某些程序的运行。因为是把广告 view 设置为 GONE 了。有些比较二的程序(不排除是故意)布局是以广告 view 为基准点的,你把它设置为 GONE 了,界面就全乱了(你把哥的广告屏蔽了,哥就不让你用了)。而且这种方法,广告还是把流量给跑了,只是没展现出来而已(就是说最终只是便宜了运营商而已)。

实现

这里我选择了比较简单的修改 hosts 方法来实现。后一种方法实现太麻烦了。这里要改 hosts,我从网上找了一个网友收集的,还是挺多的,可以先暂时用着。然后你的程序可以写成 hosts 定时从服务器上更新,这样有新广告的时候也可以更新 hosts。但是最主要的问题是 root。腾讯手机管家,要使用这个功能,需要用户已经 root 了设备。这里我们就有想办法在手机上 root 了(俗称手机上一键 root)。

root

非 fastboot 刷机模式的 root 都是通过漏洞来提升自己的进程的权限来实现的(刷机的不说了,直接刷机工具把 su, busybox, superuser.apk 弄到 system 分区去了)。这里我直接贴出几个地址吧,我也不是研究这些漏洞的。一堆老外有事没事倒腾这些东西。

Exploit Database
CVE security vulnerability datasource

上面2个都是专门收集一些 linux 的漏洞的(kernel 的比较多,不能访问的时候请自备翻墙)。漏洞都以 CEV-xxxx-xxxx 的格式编号入库例如:CVE-2009-1234(后面那个一般是提交的时间)。然后有漏洞曝光提交后,如果这些漏洞有 linux kernel 的本地(local)漏洞,就可以用来提升进程的权限,来达到临时 root 的目的。然后这些漏洞曝光后,相关厂商会修复这些漏洞,同时会有修复补丁发出来,利用之前可以看看补丁的相关内容,确定下 kernel 还能否利用这个漏洞。

然后有个老外收集了一些 android 上 kernel 的漏洞,整了一个开源的工具出来:

android_run_root_shell

下载的时候注意看上面的 READE.md 的说明,这是一个 recursive 的 git 库,附带其它别的一堆库的。代码写得还不错,能够比较方便的添加新的漏洞。我用 ndk 编译的时候(READE.md 有编译说明),报了几个错误,好像有一些漏洞的 api ndk 不支持,注释掉就好。

然后我在 rk(rockchip) 的 4.2.2 上试了下,这个漏洞有效:

Missing access checks in put_user/get_user kernel API (CVE-2013-6282)

然后就是这个工具需要获取一些 kernel 中的函数地址。记录在 sqlite 的数据中。如果没有记录的话,就会尝试从 /proc/kallsyms 中去读。这个东西是 kernel 调试用的,在编译中,关闭一个选项这个文件中就没有地址了(一般发放出去的 kernel 这个文件都中的函数地址都是 0 来的)。所以好像指望这个东西来做坏事好像不行(我很好奇它自带的数据库里面那一堆地址从哪来的)。

不过我们 kernel 是自己编译的。在编译完 kernel 后,在 kernel 根目录生成一个 System.map 的文件,这个文件里面就有 kernel 所有函数以及 static 变量的地址(十六进制的)。只要确定 kernel 版本,用相同的编译器编译一次就能得到地址了。

单个工具验证 OK,就要集成到 apk 中使用(要开始干坏事了)。首先上面那个小工具编译出来是一个 bin 文件,然后要改 hosts 文件的话,我决定把原来的 /system/etc/hosts 文件做一个软链接,指向一个普通应用可以读写的地方(例如 sdcard 的某个地方),所以需要一个 shell 脚本(在 root 的进程下调用)。然后做软件链接需要 busybox(编一个出来就好)。

东西准备好,思路就是:这些脚本、bin 文件全部放到 assert 目录下,程序安装好后,通过 AssertManager copy 到程序的 data 目录下,然后再 copy 到 /data/local/tmp 下面执行。注意,android 中 bin 文件要在 shell 下执行只有在这个目录和 system/bin, /system/xbin 下可以,所以要执行 root 工具需要 copy 到 /data/local/tmp 下,system 现在还没 root 权限咧,无法 remount 的。copy 完成后,就可以在目录下执行命令了。java 层执行命令可以用下面的代码(不用 jni 了还麻烦):

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
private void execlCommand(String path, String command) {
Process proc = null;
DataOutputStream os = null;
BufferedReader in = null;
try {
Log.d(TAG, "execl path: " + path + ", command: " + command);
// new a shell process for run out root kit
proc = Runtime.getRuntime().exec("/system/bin/sh");
// send shell command
os = new DataOutputStream(proc.getOutputStream());
os.writeBytes("echo new a shell, now execl the command ... \n");
os.writeBytes("cd " + path + " \n");
// change the file permission, java api copy file can't provider the file have execute permission
os.writeBytes("chmod 777 " + RootKit.ROOT_BIN + " \n");
os.writeBytes("chmod 777 " + RootKit.ROOT_DB + " \n");
os.writeBytes("chmod 777 " + RootKit.SHELL_TOOL + " \n");
os.writeBytes("chmod 777 " + RootKit.SHELL_COMMAND + " \n");
// and then execute the root kit
os.writeBytes("./" + RootKit.ROOT_BIN + " -c " + command + " \n");
os.writeBytes("exit \n");
os.flush();
// wait for shell execute
proc.waitFor();
// get result of execute command
Log.d(TAG, "command execl: ");
in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
Log.d(TAG, line);
}
// release resources
proc.destroy();
os.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}

从 AssertManager copy 的代码我就不贴了,网上一大堆(就是 java io 的相关函数)。

其实,如果你收集的漏洞多(多去 google 一下老外,总会有新发现的,请自带翻墙),而且没有上面的那些函数地址的限制(或是你有办法可以在机器上获取到地址),自己就可以做所谓的一键 root 工具。

文章目录
  1. 1. 拦截思路
    1. 1.1. 修改 hosts
    2. 1.2. 注入系统服务,屏蔽广告 view
  2. 2. 实现
    1. 2.1. root