Windows客户端如何透明使用DNS-over-HTTPS
0x00 现有Windows客户端程序dns查询流程
- 1.client通过系统api(gethostbyname/getaddrinfo/getaddrinfoex)发起查询dns请求
- 2.系统api会通过rpc查询本地服务dnscache是否有该host的缓存,如果缓存存在则直接返回返回host对应的ip地址,dns查询完成,如果没有则进入下一步
- 3.如果不存在该host的缓存,则首先会通过解析本地hosts文件看是否有该host对应的ip,如果存在,则直接返回该ip,否则进入下一步
- 4.如果本地hosts文件中没有该host的记录,则系统会通过发送dns udp包向本地dns服务器发起请求查询该host对应的ip,后面的dns查询过程如下图所示。

简单总结:
系统首先会通过本地缓存和hosts文件查询dns,如果缓存和hosts都没有对应的记录,会通过明文udp包向dns服务器查询。具体返回的结果受系统hosts文件及dns服务器影响较大。
0x01 现有dns查询的缺点
通过第0x0部分对现有Windows客户端程序dns查询流程的介绍,可以分析出目前dns查询的一些缺陷和风险点。
对使用客户端的用户:
跟踪:由于现有dns数据包为端口固定的明文udp包,可以很容易在网络流量中区分dns流量,并解析其中查询的域名信息。这样一些别有用心的人就可以很容易通过解析dns流量分析一个人的浏览习惯,收集敏感信息,特别是大数据火热的今天。
欺骗攻击:从开始查询到最终获取到解析结果,现有的dns查询机制需要经历较多过程,由于是明文信息,攻击者可以在任意一个环节修改解析的结果,给一个错误的解析结果,可以阻止用户访问正确的网站或则引入到攻击者想让用户访问的仍以网站。
对于开发客户端的厂商:
网络劫持:对于一些页面广告或则其他有收益业务,可能会存在通过业务被dns劫持攻击风险,使业务不可访问或则被替换成其他页面。
0x02 DNS over HTTPS是什么?
正式因为目前基于udp查询dns有各种安全缺陷,一些厂商提出了一种安全的dns查询方案DNS over Https,并且已经获得IETF支持,以RFC 8484名义发布。顾名思义DNS over HTTPS是一种通过https协议的Get请求获取域名解析的方案,简称DoH。由于基于http且与普通https请求共用443端口,如果正确部署服务器和客户端程序,理论上很难通过分析https包分析出客户端的域名请求查询结果,也不存在被篡改的风险。
0x03 客户端程序如何透明切换到DoH
既然DoH相较于基于udp包的dns查询有这么多的优势,那如何将现有的客户端程序切换到DoH呢?
我们很容易想到的过程是重写系统域名查询api,然后使用我们重写的api替换掉系统的api。但是这里会存在一个问题,目前的Windows客户端都会使用各种第三方网络库(libcurl、qt network等)及webview引擎(ieframe、libcef、qtwebview等),要想替换掉这些第三方库或则webview引擎中的系统api可能得花费很大的功夫,必须重写这部分代码,然后重新编译(某些库编译异常复杂如libcef)。并且还可能会存在一种情况是某些第三方库我们自己是没代码的,那这部分如何切换到DoH呢?
这里有一种不用重编译代码且可以对无代码的第三方库透明切换到使用DoH的简单方法,就是使用hook技术hook 系统dns查询相关系统api实现Windows客户端透明切换到DoH。可能需要hook的api如下:
需要hook的api | 影响的其他api | 影响的第三方库 |
---|---|---|
GetAddrinfoW | GetAddrinfoA | libcurl、qt network、libcef |
GetAddrinfoExW | GetAddrinfoExA | ieframe |
当然除了这两个api之外,Windows客户端程序可能还有一些使用其他api的情况(如gethostbyname/DnsQuery),但是思路相同也是hook替换掉就可以了。
在hook函数的实现过程中可能还会遇到一些小问题,如GetAddrinfoW返回结果中的addrinfo内存分配问题。正常情况下返回结果中的addrinfo由GetAddrinfoW函数在其私有堆上分配,然后调用者使用完结果后使用freeaddrinfo释放,但是当我们自己实现的时候很难获取到私有堆的句柄,这样就没办法为addrinfo分配内存,如果使用new分配内存会在freeaddrinfo释放时错误产生问题。我实现的时候通过一个简单粗暴的方式是通过调用原始的GetAddrinfoW解析localhost然后直接使用结果中的addrinfo,因为是GetAddrinfoW分配,所以最后使用freeaddrinfo释放也没问题。
除此之外在实现过程中还需要考虑很多其他因素,如dns结果优选、dns缓存设计等软件结构问题,这些可以参考微博的HTTPDNSLib的实现。
0x4 扩展,如何将操作系统切换到DoH
在0x3部分实现单个客户端进程切换到DoH,那怎样将整个Windows系统切换到DoH?
其实同样也可以借鉴0x3的思路,替换系统dns服务的查询接口,实现应该是在一个系统服务进程中,具体没研究过。。。。。
不过以及有一个更简单的思路,实现一个udp转https的本地接口,然后将Windows网络设置中的dns服务器地址修改为这个本地接口地址,并且已经有人实现了,具体看参考链接4。
0x05 参考链接
1.全局精确流量调度新思路-HttpDNS服务详解
2.DNS over HTTPS
3.HTTPDNSLib
4.[教學] 如何透過 cloudflared 使用 DNS-over-HTTPS?(Windows 適用)