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

它就像一个“中间人”,插在了你的 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 技术本身是中立的,其用途取决于使用者的意图。

合法应用场景
- 网络流量分析(安全研究/渗透测试):安全研究员可以通过 Hook App 的 DNS 请求,分析该 App 都在尝试连接哪些域名,从而了解其网络行为、是否存在敏感信息泄露或未授权的外联。
- 移动广告与追踪:一些广告 SDK 会 Hook App 的网络请求,将正常的广告请求重定向到自己的广告服务器,或植入追踪代码。
- 开发与测试:开发者在测试 App 时,需要将特定的域名(如
api.test.com)指向本地的测试服务器(如0.0.1),而不是线上的正式服务器,DNS Hook 是实现这一需求的常用方法。 - 应用防火墙/内容过滤:一些安全类 App(如 VPN、家长控制软件)可以通过 Hook DNS 来阻止 App 访问某些不良网站或域名。
非法/恶意应用场景
- 中间人攻击:这是最常见的恶意用途,攻击者通过 Hook 将银行的域名(如
mybank.com)解析到他们搭建的钓鱼网站 IP,从而窃取用户的账号密码。 - 流量劫持:将用户访问的正常网站(如新闻门户、视频网站)重定向到广告页面或恶意软件下载页面。
- 数据窃取:分析 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 为例):
- 准备工作:目标设备需要 Root,并安装 Frida Server。
- 编写 Hook 脚本:编写一个 JavaScript 脚本,目标是 Hook
java.net.InetAddress.getByName()方法。 - 执行 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接口层完成,直接 HookInetAddress可能会失效。 - 依赖运行时环境:需要 Xposed 或 Frida 等运行时支持,容易被安全软件检测。
Native 层 Hook(更底层)
当 Java 层 Hook 失效时,就需要在 Native 层进行 Hook,因为许多现代网络库(如 OkHttp)的 DNS 解析最终会调用底层的 C/C++ 函数。
核心原理:
Hook 进程中动态链接库(.so 文件)里的函数,getaddrinfo、gethostbyname 等,这些是 Linux 系统提供的标准 DNS 解析函数。
常用工具/框架:
- Frida:同样可以 Hook Native 层的函数。
- LD_PRELOAD:Linux 环境下的一个强大技术,可以在程序运行前,优先加载一个自定义的共享库,从而覆盖或 Hook 原始的库函数。
- Inline Hook:使用
frida-gum或substrate等工具,直接在内存中修改函数开头的指令,实现跳转到我们自己的 Hook 代码。
实现步骤(以 LD_PRELOAD 为例):
- 编写 Hook 函数:创建一个
.so文件,在里面实现getaddrinfo函数,并调用原始的getaddrinfo。 - 编写动态链接库:在
getaddrinfo函数中,先解析参数,判断域名,如果命中目标,则返回伪造的 IP 地址信息;否则,调用原始的getaddrinfo。 - 设置 LD_PRELOAD:在启动 App 之前,通过
export LD_PRELOAD=/path/to/your_hook.so命令,让系统优先加载你的 Hook 库。
优点:
- Hook 范围更广,可以覆盖所有调用系统
getaddrinfo的场景,包括 Java 和 Native 层。 - 更底层,更难被检测。
缺点:
- 实现复杂,需要熟悉 C/C++ 和 Linux 底层知识。
- 不同 Android 版本的系统函数可能有差异,兼容性较差。
VPN / 代理模式(系统级 Hook)
这是一种更“官方”和系统级的 Hook 方式,通过建立一个本地 VPN 代理来实现。
核心原理:
- 申请 VPN 权限:App 申请
VpnService权限。 - 建立 VPN 隧道:App 启动后,创建一个虚拟网络接口,所有 App 的网络流量都会被系统强制路由到这个 VPN 隧道。
- 拦截并处理流量:在 VPN 隧道中,App 可以读取所有经过的原始网络数据包,通过解析 DNS 请求包(通常是 UDP 端口 53),就可以直接看到所有 DNS 查询记录,然后可以选择修改它或直接返回伪造的 DNS 响应包,而无需向真正的 DNS 服务器发送请求。
优点:
- 系统级、无死角:能 Hook 所有 App 的所有网络流量,无论 App 使用什么网络库。
- 非常稳定:不依赖于具体的 App 实现。
- 权限高:可以查看和修改所有网络数据。
缺点:
- 实现最复杂:需要处理原始网络套接字和协议。
- 用户感知明显:需要用户授权 VPN,且状态栏会有 VPN
