睿诚科技协会

Android DNS Hook技术如何实现与防护?

什么是 DNS Hook?

DNS Hook,中文可以理解为 DNS 劫持DNS 拦截,其核心思想是在应用程序向系统发起 DNS 查询请求之前,拦截并修改这个请求,然后将修改后的请求(通常是查询一个恶意的或指定的 IP 地址)发送出去,最后将伪造的 DNS 查询结果返回给应用程序。

android dns hook技术
(图片来源网络,侵删)

它就像一个“中间人”,插在了你的 App 和 DNS 服务器之间。

一个正常的 DNS 查询流程: App -> 系统 DNS 缓存/解析库 -> DNS 服务器 -> 返回正确的 IP 地址 -> App

一个 DNS Hook 后的流程: App -> Hook 代码(拦截) -> 修改请求(如查询 evil.com -> 1.2.3.4) -> DNS 服务器 -> 返回伪造的 IP -> Hook 代码(返回结果) -> App


为什么要进行 DNS Hook?(应用场景)

DNS Hook 技术本身是中立的,其用途取决于使用者的意图。

android dns hook技术
(图片来源网络,侵删)

合法应用场景

  1. 网络流量分析(安全研究/渗透测试):安全研究员可以通过 Hook App 的 DNS 请求,分析该 App 都在尝试连接哪些域名,从而了解其网络行为、是否存在敏感信息泄露或未授权的外联。
  2. 移动广告与追踪:一些广告 SDK 会 Hook App 的网络请求,将正常的广告请求重定向到自己的广告服务器,或植入追踪代码。
  3. 开发与测试:开发者在测试 App 时,需要将特定的域名(如 api.test.com)指向本地的测试服务器(如 0.0.1),而不是线上的正式服务器,DNS Hook 是实现这一需求的常用方法。
  4. 应用防火墙/内容过滤:一些安全类 App(如 VPN、家长控制软件)可以通过 Hook DNS 来阻止 App 访问某些不良网站或域名。

非法/恶意应用场景

  1. 中间人攻击:这是最常见的恶意用途,攻击者通过 Hook 将银行的域名(如 mybank.com)解析到他们搭建的钓鱼网站 IP,从而窃取用户的账号密码。
  2. 流量劫持:将用户访问的正常网站(如新闻门户、视频网站)重定向到广告页面或恶意软件下载页面。
  3. 数据窃取:分析 App 的 DNS 请求,推断出用户的地理位置、兴趣偏好、使用的社交 App 等敏感信息。

如何实现 Android DNS Hook?(技术原理与实现方式)

在 Android 平台上,实现 DNS Hook 的方法多种多样,主要可以分为以下几类,其 Hook 的层级和实现难度各不相同。

Java 层 Hook(最常见)

这是最主流的方法,因为它不涉及复杂的 NDK 开发,且对大多数 App 都有效,其核心是 Hook App 进程中的网络库。

核心原理: Android App 的网络请求最终会通过 Java 层的 java.net.InetAddress 类来解析域名,Hook 这个类的 getByName() 等方法,就可以拦截所有的 DNS 查询。

常用工具/框架:

  • Xposed Framework:通过替换 /system/bin/app_process,在 App 启动时注入一个 Java Hook 框架,从而可以在不修改 App 的情况下, Hook 其内部的方法。
  • Frida:一个动态插桩工具,可以在运行时向进程注入 JavaScript 或 Python 代码,实现对函数的 Hook,Frida 非常灵活,支持 Java 和 Native 层。

实现步骤(以 Frida 为例):

  1. 准备工作:目标设备需要 Root,并安装 Frida Server。
  2. 编写 Hook 脚本:编写一个 JavaScript 脚本,目标是 Hook java.net.InetAddress.getByName() 方法。
  3. 执行 Hook:在 PC 端运行 frida -U -f com.example.targetapp -l hook.js 命令,启动目标 App 并加载脚本。

示例 Frida Hook 脚本 (hook.js):

// Hook java.net.InetAddress.getByName(String host) 方法
Java.perform(function() {
    // 获取 InetAddress 类
    var InetAddress = Java.use("java.net.InetAddress");
    // Hook getByName 方法
    InetAddress.getByName.overload('java.lang.String').implementation = function(host) {
        console.log("[DNS Hook] Original host: " + host);
        // 在这里进行你的逻辑判断和处理
        if (host === "www.example.com") {
            console.log("[DNS Hook] Hooking www.example.com to 1.2.3.4");
            // 返回一个伪造的 InetAddress 对象
            return InetAddress.getByName("1.2.3.4");
        }
        // 如果不是目标域名,则调用原始方法
        return this.getByName(host);
    };
});

优点

  • 实现相对简单,无需修改 App。
  • 可以 Hook 纯 Java 网络请求(如 HttpURLConnection, OkHttp 3.x 以下版本)。

缺点

  • 对现代网络库无效:对于使用 OkHttp 3.x 及以上版本、Jetpack Networking 等现代网络库的 App,DNS 查询可能在 Dns 接口层完成,直接 Hook InetAddress 可能会失效。
  • 依赖运行时环境:需要 Xposed 或 Frida 等运行时支持,容易被安全软件检测。

Native 层 Hook(更底层)

当 Java 层 Hook 失效时,就需要在 Native 层进行 Hook,因为许多现代网络库(如 OkHttp)的 DNS 解析最终会调用底层的 C/C++ 函数。

核心原理: Hook 进程中动态链接库(.so 文件)里的函数,getaddrinfogethostbyname 等,这些是 Linux 系统提供的标准 DNS 解析函数。

常用工具/框架:

  • Frida:同样可以 Hook Native 层的函数。
  • LD_PRELOAD:Linux 环境下的一个强大技术,可以在程序运行前,优先加载一个自定义的共享库,从而覆盖或 Hook 原始的库函数。
  • Inline Hook:使用 frida-gumsubstrate 等工具,直接在内存中修改函数开头的指令,实现跳转到我们自己的 Hook 代码。

实现步骤(以 LD_PRELOAD 为例):

  1. 编写 Hook 函数:创建一个 .so 文件,在里面实现 getaddrinfo 函数,并调用原始的 getaddrinfo
  2. 编写动态链接库:在 getaddrinfo 函数中,先解析参数,判断域名,如果命中目标,则返回伪造的 IP 地址信息;否则,调用原始的 getaddrinfo
  3. 设置 LD_PRELOAD:在启动 App 之前,通过 export LD_PRELOAD=/path/to/your_hook.so 命令,让系统优先加载你的 Hook 库。

优点

  • Hook 范围更广,可以覆盖所有调用系统 getaddrinfo 的场景,包括 Java 和 Native 层。
  • 更底层,更难被检测。

缺点

  • 实现复杂,需要熟悉 C/C++ 和 Linux 底层知识。
  • 不同 Android 版本的系统函数可能有差异,兼容性较差。

VPN / 代理模式(系统级 Hook)

这是一种更“官方”和系统级的 Hook 方式,通过建立一个本地 VPN 代理来实现。

核心原理:

  1. 申请 VPN 权限:App 申请 VpnService 权限。
  2. 建立 VPN 隧道:App 启动后,创建一个虚拟网络接口,所有 App 的网络流量都会被系统强制路由到这个 VPN 隧道。
  3. 拦截并处理流量:在 VPN 隧道中,App 可以读取所有经过的原始网络数据包,通过解析 DNS 请求包(通常是 UDP 端口 53),就可以直接看到所有 DNS 查询记录,然后可以选择修改它或直接返回伪造的 DNS 响应包,而无需向真正的 DNS 服务器发送请求。

优点

  • 系统级、无死角:能 Hook 所有 App 的所有网络流量,无论 App 使用什么网络库。
  • 非常稳定:不依赖于具体的 App 实现。
  • 权限高:可以查看和修改所有网络数据。

缺点

  • 实现最复杂:需要处理原始网络套接字和协议。
  • 用户感知明显:需要用户授权 VPN,且状态栏会有 VPN
分享:
扫描分享到社交APP