Windows域名解析保护机制深度分析

2006年4月Dave Korn在[Full-disclosure]邮件列表中发言,指责微软干扰hosts文件的使用。一般FQDN的解析顺序都是hosts文件优先,但XP SP2在解析某些FQDN时不再尊重hosts中的设置。XP SP2的dnsapi.dll中固化了一批FQDN,据Dave Korn测试,至少如下两个FQDN被特殊对待了

go.microsoft.com
office.microsoft.com

即使在hosts文件中将这两个FQDN解析成127.0.0.1,也会成功访问它们。当年我测过,XP SP1尚无此行为。

现在要是放狗搜,有很多相关讨论,不过都流于表象,并未从逆向工程角度阐述此事。本来我也不关心这事的技术细节,但前几天搞Win10 RPC调试时拿Dnscache服务做靶子,遇到hosts中的”download.windowsupdate.com”无法生效,好奇心起,逆向工程了一把。本文没有直接给结论,展示一下探索过程。

Guest环境如下

Win10

Win10企业版2016 LTSB 1607(OS Build 14393.4704)

dnsapi.dll

10.0.14393.4350 (rs1_release.210407-2154)

dnsrslvr.dll

10.0.14393.4350 (rs1_release.210407-2154)

在Guest中

$ notepad c:\windows\system32\drivers\etc\hosts

127.0.0.1 settings-win.data.microsoft.com
127.0.0.1 sls.update.microsoft.com
127.0.0.1 fe2.update.microsoft.com
127.0.0.1 download.windowsupdate.com
127.0.0.1 au.download.windowsupdate.com
127.0.0.1 ctldl.windowsupdate.com
127.0.0.1 geo.prod.do.dsp.mp.microsoft.com

无法阻止Win10更新。简单测试表明,如下FQDN不受hosts影响

sls.update.microsoft.com
fe2.update.microsoft.com
download.windowsupdate.com
au.download.windowsupdate.com
ctldl.windowsupdate.com

下列FQDN不受保护

settings-win.data.microsoft.com
geo.prod.do.dsp.mp.microsoft.com

假设Dnscache服务位于svchost(564)中,调试之

bp dnsrslvr!Cache_FindEntry “du @rcx”

该函数第1形参是FQDN。假设断下时正在请求解析”download.windowsupdate.com”,gu之后rax为0,表示没找到。从而确认DNS Cache中没有相应FQDN,表明hosts中条目未被加入DNS Cache。可以换其他非微软FQDN测试,hosts中条目正常进入DNS Cache。编辑hosts,保存,让文件产生时间戳变动。用Process Monitor监控svchost(564),发现有

svchost.exe 564 NotifyChangeDirectory C:\Windows\System32\drivers\etc SUCCESS Filter: FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME

应该是提醒Dnscache重读hosts文件。

ipconfig /flushdns
ping download.windowsupdate.com

用Process Monitor监控svchost(564),发现Dnscache服务读取hosts,调用栈如下

0 FLTMGR.SYS FltpPerformPreCallbacksWorker+0x480 0xfffff8054c611bdc
1 FLTMGR.SYS FltpPassThroughInternal+0x88 0xfffff8054c6039e8
2 FLTMGR.SYS FltpPassThrough+0x1a6 0xfffff8054c6030c6
3 FLTMGR.SYS FltpDispatch+0x9e 0xfffff8054c602e6e
4 ntoskrnl.exe IopSynchronousServiceTail+0x1a0 0xfffff802b80150f0
5 ntoskrnl.exe NtReadFile+0x670 0xfffff802b8013480
6 ntoskrnl.exe KiSystemServiceCopyEnd+0x13 0xfffff802b7d75103
7 ntdll.dll NtReadFile+0x14 0x7fff88d95d44
8 KERNELBASE.dll ReadFile+0x74 0x7fff86095014
9 DNSAPI.dll hostsFileSniffBom+0x5b 0x7fff8483d627
10 DNSAPI.dll HostsFile_Open+0x14e 0x7fff8483d47e
11 dnsrslvr.dll LoadHostFileIntoCache+0x53 0x7fff7c356473
12 dnsrslvr.dll Cache_Initialize+0xda 0x7fff7c3563ba
13 dnsrslvr.dll Cache_Lock+0x80 0x7fff7c353110
14 dnsrslvr.dll Cache_GetRecordsForRpc+0xa0 0x7fff7c3524b0
15 dnsrslvr.dll Cache_Query+0xab 0x7fff7c3523eb
16 DNSAPI.dll Query_SingleNamePrivate+0xf4 0x7fff848330f4
17 DNSAPI.dll Query_SingleNameDualAddr+0xf6 0x7fff84835446
18 DNSAPI.dll Query_NextName+0x38b 0x7fff8482d93b
19 DNSAPI.dll Query_Main+0x904 0x7fff848315e4
20 dnsrslvr.dll ResolverQuery+0x103 0x7fff7c354863
21 dnsrslvr.dll R_ResolverQuery+0x233 0x7fff7c354733
22 RPCRT4.dll Invoke+0x73 0x7fff8817a583
23 RPCRT4.dll Ndr64AsyncServerWorker+0x392 0x7fff881d6162
24 RPCRT4.dll DispatchToStubInCNoAvrf+0x24 0x7fff8814a284
25 RPCRT4.dll RPC_INTERFACE::DispatchToStubWorker+0x1bd 0x7fff8814919d
26 RPCRT4.dll RPC_INTERFACE::DispatchToStub+0xcb 0x7fff88149a4b
27 RPCRT4.dll LRPC_SCALL::DispatchRequest+0x34c 0x7fff881310ac
28 RPCRT4.dll LRPC_SCALL::HandleRequest+0x2bc 0x7fff8813152c
29 RPCRT4.dll LRPC_ADDRESS::HandleRequest+0x36c 0x7fff8811ae1c
30 RPCRT4.dll LRPC_ADDRESS::ProcessIO+0x91b 0x7fff8811c67b
31 RPCRT4.dll LrpcIoComplete+0xaa 0x7fff88143a2a
32 ntdll.dll TppAlpcpExecuteCallback+0x25e 0x7fff88d0d35e
33 ntdll.dll TppWorkerThread+0x8d9 0x7fff88d0ecc9
34 KERNEL32.DLL BaseThreadInitThunk+0x14 0x7fff885b84d4
35 ntdll.dll RtlUserThreadStart+0x21 0x7fff88d41791

从前述调用栈中关注到”DNSAPI!HostsFile_*”

x DNSAPI!HostsFile_*
00007fff84841c10 DNSAPI!HostsFile_Query (void) 00007fff8483cda0 DNSAPI!HostsFile_ReadLine (void)
00007fff8483d710 DNSAPI!HostsFile_Close (void) 00007fff8483d330 DNSAPI!HostsFile_Open (void)
00007fff`8485bfe8 DNSAPI!hostsFile_ScreenName (hostsFile_ScreenName)

IDA中静态分析DNSAPI!HostsFile_ReadLine,注意到DNSAPI!BuildHostfileRecords。用wt分析DNSAPI!BuildHostfileRecords内部执行情况,搞个对比实验,一次是微软域名,另一次是非微软域名,用.logopen/.logclose记录wt输出。

对比BuildHostfileRecords_bad.txt、BuildHostfileRecords_ok.txt,很容易发现
分叉点

25 0 [ 0] DNSAPI!BuildHostfileRecords
call at 00007fff8483bfed
777 0 [ 1] DNSAPI!Dns_NameCompareEx rax = 0
37 777 [ 0] DNSAPI!BuildHostfileRecords
call at 00007fff8483bfed
821 0 [ 1] DNSAPI!Dns_NameCompareEx rax = 2

前者第二次调用DNSAPI!Dns_NameCompareEx时返回2,后者5次调用DNSAPI!Dns_NameCompareEx均返回0。

用IDA查看DNSAPI!BuildHostfileRecords+0x7d附近代码,DNSAPI!Dns_NameCompareEx

对源自hosts文件的RightName进行检查,LeftName源自DNSAPI!DomainScreenList[5]

windowsupdate.microsoft.com
windowsupdate.com
microsoftupdate.com
download.microsoft.com
update.microsoft.com

前述DNSAPI!Dns_NameCompareEx返回2那次

LeftName windowsupdate.com // 源自DNSAPI!DomainScreenList[5]
RightName download.windowsupdate.com // 源自hosts
CharSet DnsCharSetUnicode(1)
Return DnsNameCompareLeftParent(2)

正是这次匹配使得download.windowsupdate.com未能进入DNS Cache。

除了DNSAPI!DomainScreenList[5],还有DNSAPI!HostsScreenList[18]

microsoft.com
www.microsoft.com
support.microsoft.com
wustats.microsoft.com
download.microsoft.com
microsoftupdate.microsoft.com
office.microsoft.com
msdn.microsoft.com
go.microsoft.com
msn.com
www.msn.com
msdn.com
www.msdn.com
technet.microsoft.com
technet.com
www.technet.com
activation-v2.sls.microsoft.com
validation-v2.sls.microsoft.com

当DNSAPI!DomainScreenList[5]均不匹配时,继续检查DNSAPI!HostsScreenList[18]。

u DNSAPI!BuildHostfileRecords+0x41 l 6
00007fff8483bfb1 488b05f8430500 mov rax,qword ptr [DNSAPI!DomainScreenList (00007fff848903b0)]

00007fff8483bfcb 4885c0 test rax,rax 00007fff8483bfce 743b je DNSAPI!BuildHostfileRecords+0x9b (00007fff`8483c00b)

u DNSAPI!BuildHostfileRecords+0x9b l 5
00007fff8483c00b 4c8b05ce430500 mov r8,qword ptr [DNSAPI!HostsScreenList (00007fff848903e0)]

00007fff8483c01e 4d85c0 test r8,r8 00007fff8483c021 7466 je DNSAPI!BuildHostfileRecords+0x119 (00007fff`8483c089)

从上述两段代码逻辑看,只要DNSAPI!DomainScreenList[0]、
DNSAPI!HostsScreenList[0]为NULL,这两种微软域名保护检查将被绕过。

Patch

eq DNSAPI!DomainScreenList 0
eq DNSAPI!HostsScreenList 0

假设hosts修改如下

$ notepad c:\windows\system32\drivers\etc\hosts

127.0.0.1 sls.update.microsoft.com
127.0.0.1 fe2.update.microsoft.com
127.0.0.1 download.windowsupdate.com

ipconfig /flushdns

之后Guest的”Checking for updates”报错

We couldn’t connect to the update service. We’ll try again later, or you
can check now. If it still doesn’t work, make sure you’re connected to the
Internet.

热Patch绕过微软域名保护需要上调试器。若不想上调试器,可在Guest上启动一个定制版DNS Server,由它负责Guest的所有FQDN解析;当它发现需要劫持的FQDN出现,直接返回指定IP,否则转发给其他正常DNS Server。这种工具很多,以前我分享过一个superdns.exe。

$ vi some.hosts

127.0.0.1 ^.+.update.microsoft.com.$
127.0.0.1 ^.+.windowsupdate.com.$

将Guest的DNS Server指向127.0.0.1,执行superdns

superdns.exe -q -m udp -i 192.168.65.2 some.hosts

假设192.168.65.2是其他正常DNS Server,若是VMware环境,这就是Host所在。some.hosts中劫持了正则表达式匹配之FQDN。-q表示安静模式,去掉后可以看到调试信息,比如请求解析的FQDN以及响应报文解码后的内容。这样干,效果同热Patch。去掉-q,确认Win10自动更新首先检查

sls.update.microsoft.com

实际只需劫持这一个FQDN即可干扰Win10自动更新。该FQDN会被解析成不同IP,并不固定,否则可在PFW中对出站请求的目标IP设置阻断规则。

版权声明

本站“技术博客”所有内容的版权持有者为绿盟科技集团股份有限公司(“绿盟科技”)。作为分享技术资讯的平台,绿盟科技期待与广大用户互动交流,并欢迎在标明出处(绿盟科技-技术博客)及网址的情形下,全文转发。
上述情形之外的任何使用形式,均需提前向绿盟科技(010-68438880-5462)申请版权授权。如擅自使用,绿盟科技保留追责权利。同时,如因擅自使用博客内容引发法律纠纷,由使用者自行承担全部法律责任,与绿盟科技无关。

Spread the word. Share this post!

Meet The Author

C/ASM程序员

Leave Comment